docs: add ADR-0009 for Akrites CDP public API authentication (CM-1333)#4345
docs: add ADR-0009 for Akrites CDP public API authentication (CM-1333)#4345joanagmaia wants to merge 6 commits into
Conversation
Signed-off-by: Joana Maia <jmaia@contractor.linuxfoundation.org>
PR SummaryLow Risk Overview The chosen default is M2M It also documents an open assumption (Akrites AWS workload role, KMS/CMK for cross-account decrypt) and a Reviewed by Cursor Bugbot for commit 2bb785c. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Pull request overview
Adds a proposed ADR for Akrites machine-to-machine authentication to the CDP public API.
Changes:
- Defines the Auth0 token exchange and authorization flow.
- Outlines implementation across four affected repositories.
- Adds ADR-0009 to the ADR index.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
docs/adr/0009-akrites-cdp-public-api-authentication.md |
Documents the proposed authentication architecture and implementation plan. |
docs/adr/README.md |
Adds ADR-0009 to the index. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Align proposed approach with LF-owned Secrets Manager cross-account read model, prune Alternatives to shared client_secret fallback only, switch scopes to read:packages + read:advisories + read:maintainers, and fix Mermaid diagram parse errors. Signed-off-by: Joana Maia <jmaia@contractor.linuxfoundation.org>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 10 comments.
Comments suppressed due to low confidence (3)
docs/adr/0009-akrites-cdp-public-api-authentication.md:66
- The PR description says this integration will use a dedicated resource server and a
read:akritesscope, but the ADR decides to reusecdp_public_apiwith three different scopes. These are materially different authentication designs; update either the PR description or this decision so reviewers know which architecture is actually proposed.
Authenticate Akrites against the **existing `cdp_public_api` resource
server**, gated by an **`azp` allowlist middleware** in CDP (sole consumer
identity gate) and domain scopes **`read:packages`**, **`read:advisories`**,
**`read:maintainers`** on the granted token. Distribute the RSA private
docs/adr/README.md:18
- The index date does not match the ADR's
2026-07-21date, leaving conflicting metadata for the same decision.
| [ADR-0009](./0009-akrites-cdp-public-api-authentication.md) | Akrites → CDP public API authentication | proposed | 2026-07-14 |
docs/adr/0009-akrites-cdp-public-api-authentication.md:314
- The ADR ends without the mandatory
## Consequencessection and its### Positive,### Negative, and### Riskssubsections required by.claude/rules/adr-format.md:16-31. Capture the accepted trade-offs and mitigations before this ADR is merged.
LF rather than auto-retry; do not tight-loop.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (8)
docs/adr/0009-akrites-cdp-public-api-authentication.md:59
- The referenced companion document is not included in this PR or present in the repository, despite the PR description saying it is added. Add
docs/akrites-cdp-auth-highlevel.md, or remove this reference and update the PR description.
High-level overview: `docs/akrites-cdp-auth-highlevel.md`
(mirror of the internal DSP page).
docs/adr/0009-akrites-cdp-public-api-authentication.md:236
- This route-level scope check does not replace the existing nested checks.
akrites-external/index.ts:43still also requiresread:stewardships, and line 66 requiresread:maintainer-roles; neither is granted here, so the documented Akrites token will receive 403s for package and contact endpoints. Update those subrouter guards and the OpenAPI contract to use the new domain scopes; otherwise grant the existing scopes.
requireScopes(
[SCOPES.READ_PACKAGES, SCOPES.READ_ADVISORIES, SCOPES.READ_MAINTAINERS],
'all',
),
docs/adr/0009-akrites-cdp-public-api-authentication.md:254
- ECS
ValueFrominjects the secret only when a task starts; a running task does not receive rotated values. That option cannot support the documentedinvalid_clientre-read without redeployment. Require an SDK/runtime fetch through the task or pod role, or explicitly document forced task deployment on rotation.
full LF secret ARN. Prod options: ECS `ValueFrom` on the task definition
with the LF ARN, or EKS External Secrets Operator + IRSA. Dev:
1Password via the LF-provided vault item.
docs/adr/0009-akrites-cdp-public-api-authentication.md:175
- A cross-account Secrets Manager secret cannot use the AWS-managed
aws/secretsmanagerkey, so a customer-managed KMS key andkms:Decryptpermissions are mandatory, not conditional. Also,kms:Decryptbelongs in the KMS key policy and caller identity policy—not the secret resource policy. Apply the same correction to the optional-CMK wording earlier in the ADR.
**Resource policy on the LF secret** — grants Akrites' workload IAM role
`secretsmanager:GetSecretValue` + `secretsmanager:DescribeSecret`. Deny
wildcards. If the secret is CMK-encrypted, the KMS key policy must also
allow `kms:Decrypt` for that role. Akrites' AWS account ID + workload role
ARN required from the Akrites team before the resource policy can be
docs/adr/0009-akrites-cdp-public-api-authentication.md:257
- The Auth0 request description omits required form fields (
client_id,audience, andclient_assertion), and the assertion step omits Auth0's required claims. Following these steps literally cannot complete the token exchange; document the complete request and the requirediss,sub,aud,exp, and uniquejticlaims.
2. Build and sign `client_assertion` JWT (RS256).
3. POST to Auth0 `/oauth/token` with `grant_type=client_credentials` +
`client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer`.
docs/adr/0009-akrites-cdp-public-api-authentication.md:264
- This ADR ends after Alternatives and omits the mandatory
## Consequencessection with### Positive,### Negative, and### Risks. Add it as required by.claude/rules/adr-format.md:16-31; rotation ordering, cross-account availability, and the consumer-allowlist operational burden should be captured there.
## Alternatives Considered
docs/adr/README.md:18
- The index date does not match the ADR's
2026-07-21date, making the ADR index inaccurate.
| [ADR-0009](./0009-akrites-cdp-public-api-authentication.md) | Akrites → CDP public API authentication | proposed | 2026-07-14 |
docs/adr/0009-akrites-cdp-public-api-authentication.md:7
- The Context section substantially exceeds the required 2–5 sentences in
.claude/rules/adr-format.md:25. Keep only the situation and decision forces here, and move implementation details into clearly named sections after the Decision.
## Context
Fix audience contradiction (shared AUTH0_CONFIG across public routes), correct public URL path to /api/v1/akrites-external, reword the new route mount as an insertion before the 404 handler, soften KMS wording with a DevOps validation note, expand Akrites-side delivery patterns to reflect ValueFrom rotation constraints, spell out the /oauth/token request body, drop the stale highlevel doc reference, and bump the ADR index date. Signed-off-by: Joana Maia <jmaia@contractor.linuxfoundation.org>
eb630a6 to
70f1c0b
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (4)
docs/adr/0009-akrites-cdp-public-api-authentication.md:361
- The ADR is missing the mandatory
## Consequencessection with### Positive,### Negative, and### Risks..claude/rules/adr-format.md:27-30requires all of these before an ADR can be added, so please document the accepted trade-offs and mitigations.
LF rather than auto-retry; do not tight-loop.
docs/adr/0009-akrites-cdp-public-api-authentication.md:7
- This Context contains substantially more than the required 2–5 sentences and mixes background with detailed implementation guidance.
.claude/rules/adr-format.md:25requires a concise Context; move the KMS, route, and scope details into dedicated sections so the decision record remains scannable.
## Context
docs/adr/0009-akrites-cdp-public-api-authentication.md:1
- The PR description says it adds
docs/akrites-cdp-auth-highlevel.md, but that companion document is absent from the changed files and repository. Either include the promised mirror or update the PR description so the documented scope matches the change.
# ADR-0009: Akrites → CDP public API authentication
docs/adr/0009-akrites-cdp-public-api-authentication.md:247
- This mount-level
allcheck, combined with removing inner scope guards, does not implement the domain-level handler isolation claimed at lines 62–64: every admitted token must carry all three scopes and can reach every handler. Either enforce each scope on its corresponding subrouter, or explicitly document that these scopes are an all-or-nothing route bundle rather than independent domain authorization.
requireScopes(
[SCOPES.READ_PACKAGES, SCOPES.READ_ADVISORIES, SCOPES.READ_MAINTAINERS],
'all',
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (5)
docs/adr/0009-akrites-cdp-public-api-authentication.md:1
- The PR description says this change adds
docs/akrites-cdp-auth-highlevel.md, but that companion document is absent from the changed files and the checkout. Either add the promised document or update the PR description so the delivered scope is accurate.
# ADR-0009: Akrites → CDP public API authentication
docs/adr/0009-akrites-cdp-public-api-authentication.md:7
- This Context is far beyond the required 2–5 sentences and mixes implementation instructions, open questions, and decision details into the problem statement. Please keep the Context concise and move the detailed material into focused sections;
.claude/rules/adr-format.md:25makes the 2–5 sentence structure mandatory for ADRs.
## Context
docs/adr/0009-akrites-cdp-public-api-authentication.md:78
- Cross-account Secrets Manager access cannot use the AWS-managed
aws/secretsmanagerkey; AWS requires a customer-managed KMS key plus aligned key and identity policies. Leaving the CMK as conditional allows an implementation that cannot retrieve the secret, so make this a requirement rather than a DevOps validation item throughout the ADR.
If the secret is encrypted with a customer-managed KMS key (likely
required for cross-account decrypt — LF DevOps to confirm), the KMS key
policy must also grant `kms:Decrypt` to that role. Akrites reads
docs/adr/0009-akrites-cdp-public-api-authentication.md:256
- This is not the repository's current state:
akritesExternalRouter()and its mount already exist, and the router has four endpoint-specificrequireScopesguards (index.ts:43,54,66,76). The proposed mount-wide all-scopes guard also conflicts with the existing OpenAPI security declarations. Document this as a migration, decide whether per-domain authorization is intentionally being removed, and include the required OpenAPI update.
`akritesExternalRouter()` is a **new module** at
`backend/src/api/public/v1/akrites-external/index.ts` — separate from the
existing `akritesRouter` and free of its inner scope guards, so the
mount-level `requireScopes` above is authoritative for this route.
docs/adr/0009-akrites-cdp-public-api-authentication.md:311
- The ADR ends after Alternatives and omits the mandatory
## Consequencessection with### Positive,### Negative, and### Risks. Add those sections before merging; they are required by.claude/rules/adr-format.md:28and are especially important for the stated cross-account and rotation trade-offs.
## Alternatives Considered
…WS caller identity (CM-1333) Signed-off-by: Joana Maia <jmaia@contractor.linuxfoundation.org>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (7)
docs/adr/0009-akrites-cdp-public-api-authentication.md:365
- The ADR ends without the mandatory
## Consequencessection and its### Positive,### Negative, and### Riskssubsections..claude/rules/adr-format.md:27-31requires all four headings, so this ADR does not satisfy the repository template yet.
LF rather than auto-retry; do not tight-loop.
docs/adr/0009-akrites-cdp-public-api-authentication.md:7
- This section is substantially longer than the required 2–5 sentence Context in
.claude/rules/adr-format.md:25. Please move implementation detail into later supporting sections and keep Context focused on the forces that prompted the decision.
## Context
docs/adr/0009-akrites-cdp-public-api-authentication.md:68
- The Decision is five sentences, while
.claude/rules/adr-format.md:26limits it to 1–3 sentences. Condense the selected authentication, authorization, and secret-delivery choice here; retain the conditional and implementation details in supporting sections.
## Decision
docs/adr/0009-akrites-cdp-public-api-authentication.md:248
- Requiring all three scopes at the mount and then removing the subrouter checks makes authorization all-or-nothing: every accepted token can call every domain handler. That contradicts lines 62–64, which say domain scopes constrain which handlers are reachable, and prevents least-privilege package-, advisory-, or maintainer-only access. Keep the
azpgate global and enforce each domain scope on its relevant subrouter, or explicitly revise the decision if all-or-nothing access is intended.
requireScopes(
[SCOPES.READ_PACKAGES, SCOPES.READ_ADVISORIES, SCOPES.READ_MAINTAINERS],
'all',
),
docs/adr/0009-akrites-cdp-public-api-authentication.md:221
config.get('akritesExternal.clientId')throws during module import when the environment variable is absent. Because the plan only adds the custom-environment mapping, any local, test, or deployed environment not populated before this code rolls out will fail backend startup. Include the required environment propagation and rollout order, or make configuration optional while keeping the route fail-closed.
Add:
```ts
export const AKRITES_EXTERNAL_CLIENT_ID: string = config.get<string>('akritesExternal.clientId')
**docs/adr/0009-akrites-cdp-public-api-authentication.md:313**
* This retry procedure cannot work for the documented ECS `ValueFrom` option: as lines 281–282 note, a running task cannot re-read the rotated value and must be replaced, so an in-process retry uses the same stale key. Specify separate recovery behavior for each delivery mode and update the sequence diagram accordingly; ECS needs task replacement rather than re-fetch-and-retry.
- On
invalid_client: discard the cached key → re-fetch from the secret
store (SDK read, or task/pod refresh depending on the pattern chosen
in step 1) → retry the token exchange once. LF rotates the keypair
without notice.
**docs/adr/0009-akrites-cdp-public-api-authentication.md:1**
* The PR description says this change also adds `docs/akrites-cdp-auth-highlevel.md`, but that companion document is absent from the changed files. Add the promised document or update the PR description so reviewers and consumers have an accurate deliverable list.
ADR-0009: Akrites → CDP public API authentication
</details>
Summary
docs/akrites-cdp-auth-highlevel.md(mirror of the internal DSP page).auth0-terraform,lfx-secrets-management,crowd.dev, and Akrites (external).azp+ scope validation.Context
Akrites is a new external consumer needing read-only access to CDP's public API through a new
/akrites-externalroute on the existing public API listener. The ADR formalizes the M2M auth strategy:cdp_public_apiresource server (not a new one) — a new RS would mean minting a second audience for the same logical API with no separate service backing it.azpallowlist middleware on the/akrites-externalroute as the sole gate for consumer identity. Scopes alone do not identify the consumer.read:packages,read:advisories,read:maintainerson the granted token.read:packagesalready exists on the RS and is reused (also granted tolfxone_cdp);read:advisories+read:maintainersare new.private_key_jwtauthentication, with the private key in an LF-owned AWS Secrets Manager entry. A resource (item) policy on that secret grants Akrites' workload IAM roleGetSecretValue(+kms:Decryptif CMK). Akrites reads cross-account — same pattern as cross-account S3.lfx-secrets-managementon the standard cadence. Akrites reacts toinvalid_clientby re-reading the latest key version and retrying once.Open decision
Where Akrites runs (AWS account + workload IAM role) is not yet confirmed — deferred to the week of 2026-07-27. If Akrites has no AWS account (or no workload role for LF to grant access to via the item policy), we fall back to a shared
client_secretvariant: same client, same scopes, same CDP middleware chain — only thelfx-secrets-managementsync source flips fromauth0_jwtback toauth0, and rotation becomes out-of-band. Fallback is documented in both docs; CDP-side code is unaffected either way.Companion PRs
auth0-terraform— newAkrites Externalclient, new scopes oncdp_public_api, new grant: PR #350 (draft) — branchfeat/CM-1333-akrites-external-cdp-auth.lfx-secrets-management(to be implemented) — sync entry rotating the client toprivate_key_jwt, destinations 1Password + LF-owned AWS Secrets Manager: branchfeat/CM-1333-akrites-external-secret-sync(PR pending).Jira: CM-1333
🤖 Generated with Claude Code