Repository navigation
Conversation
|
| Topic | Details | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Security and reliability | Protect exchanged tokens and record keys across request bodies, response debugging, configuration dumps, GitHub Actions logs, and error paths while adding configurable retries, audience normalization, and end-to-end tests for precedence, failures, masking, JSON output, and revocation.Modified files (5)
Latest Contributors(2)
| |||||||||
| OIDC authentication flow | Implement OIDC token discovery, project-scoped RFC 8693 exchange, command integration, precedence rules, and RFC 7009 cleanup for upload, cache, run, cancel, and session workflows.Modified files (10)
Latest Contributors(2)
| |||||||||
| CLI configuration UX | Extend configuration validation and user-facing command help with OIDC options, project requirements, deployment-specific audience guidance, and compatibility behavior for configured record or API keys.Modified files (5)
Latest Contributors(2)
|
Merger ✅ Ready to Merge
MERGE: the OIDC record-key flow is implemented, CI ran successfully, and all review discussions are settled.
When --key and CURRENTS_RECORD_KEY are not set, commands that take a record key exchange an OIDC token for a short-lived record key with POST /oidc/token (OAuth 2.0 Token Exchange, RFC 8693). Token sources, in order: - CURRENTS_OIDC_TOKEN: a token the job gets itself, on any CI provider. Its audience must be the Currents URL. - GitHub Actions: requested with the Currents URL as the audience, with retries, when the job has `id-token: write`. The exchange runs once per process in the preAction hook, and is skipped when an API key is set on a command that accepts one. The key is revoked with POST /oidc/revoke (RFC 7009) when the command ends; a failed revoke only writes a debug line. --debug never prints the tokens or the key. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011baD3jW7wFPFbKF1o7FCeC
568d700 to
7897262
Compare
Every command that takes --key also takes --oidc-token <token>, with CURRENTS_OIDC_TOKEN as its environment variable. Order: --key or CURRENTS_RECORD_KEY, then --oidc-token, then CURRENTS_OIDC_TOKEN, then GitHub Actions. An empty value counts as unset. The preAction hook clears the value from the options after reading it, so it is not copied into the config of a command or printed with --debug. oidcToken is also in the masked field names. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011baD3jW7wFPFbKF1o7FCeC
|
Security review at The issued record key is not masked in CI logs A key from a CI secret is masked by the provider. A key fetched at runtime is not. The CLI masks its own debug output, but a later step or tool that prints the command's options or environment shows the key. On GitHub Actions, printing
A value passed as a flag is visible in the process list and in CI logs that echo commands. The option also reads
The exit hook posts the key to Checked and fine: only GitHub Actions is used automatically, with the Currents URL as the requested audience; an API key or a record key wins over OIDC; Generated by Claude Code |
|
Code review follow-up, checked at Tokens from The exchange in currents-dev/currents#4948 refuses a token whose The fix belongs on the server (details on #4948). GitHub Actions is not affected, because the CLI requests a fresh token each time. Generated by Claude Code |
- Commands with --json print logs to stderr before the exchange, so the retry warnings of the exchange do not come before the JSON on stdout. - On GitHub Actions, print ::add-mask::<key> on stderr after the exchange. A key fetched at run time is not a secret the runner knows, so a later step that prints it would show it. - The --oidc-token help says to prefer CURRENTS_OIDC_TOKEN: a flag is visible in the process list and in logs that echo commands. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011baD3jW7wFPFbKF1o7FCeC
The exchange is scoped to one project, so POST /oidc/token carries a project_id form field from --project-id or CURRENTS_PROJECT_ID. Without a project ID the CLI does not call the exchange and says which flag and variable to set. cache set, cache get, session attach and session share get --project-id, read only for the exchange. It is not required when a record key or an API key is set. session attach and share do not copy it into their config. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011baD3jW7wFPFbKF1o7FCeC
Baz withdrew its approval because this pull request is no longer ready to merge.
Owner: @agoldis
Linear: project P-ENG-132 (OIDC record keys for CI)
When no record key is set, the
currentsCLI exchanges an OIDC token for a record key that expires after 6 hours. Customers no longer need to storeCURRENTS_RECORD_KEYas a secret. This also covers Jest and node:test results, becausecurrents run uploadsends them.Do not merge or release until the server routes
POST /oidc/tokenandPOST /oidc/revokeare deployed and turned on (currents-dev/currents#4948). Until then, nothing changes for anyone with a key. Without the revoke route the revoke fails quietly and the key lives until it expires.Token sources
Used only when no key is set. The first one that applies wins:
--oidc-token <token>, thenCURRENTS_OIDC_TOKEN: a token the job gets itself, on any CI provider. For GitLab CI:id_tokens: CURRENTS_OIDC_TOKEN: aud: <Currents URL>. Prefer the variable: a flag is visible in the process list and in logs that echo commands.permissions: id-token: write: the CLI requests a token withACTIONS_ID_TOKEN_REQUEST_URL.The audience of the token is the origin of the Currents URL (
CURRENTS_API_URL, defaulthttps://cy.currents.dev), so a token for one deployment is not accepted by another.--keyorCURRENTS_RECORD_KEYalways wins.Exchange and revoke
POST /oidc/token, OAuth 2.0 Token Exchange (RFC 8693). Form bodygrant_type,subject_token,subject_token_typeandproject_id; the response hasaccess_token(the record key) andexpires_in. Errors showerror_description, thenerror, then the HTTP status text.POST /oidc/revoke(RFC 7009) with the key. One attempt, 5 seconds, no retries. A failure only writes a debug line and never changes the exit code. A key from--keyorCURRENTS_RECORD_KEYis not revoked.project_idcomes from--project-idorCURRENTS_PROJECT_ID. Without it the CLI does not call the exchange and says which flag and variable to set.cache set,cache get,session attachandsession sharehave--project-idonly for this; it is not required when a record key or an API key is set. The project ID is not masked in--debugoutput.preActionhook and fills thekeyoption before validation, so no command changed. Commands with--keyget it:run upload,run cancel,run attach,cache get,cache set, andsession start,attachandshare.run attachandsession *skip the exchange when an API key is set. Commands with only--api-key(api,run get,run share) are unchanged.--json, all logs go to stderr before the exchange.::add-mask::<key>on stderr, so a later step that prints the key shows***.--debugmaskssubject_token,token,oidcTokenandaccess_token, and--oidc-tokenis cleared from the options after it is read, so it does not reach the config dump.@currents/jestand@currents/node-test-reporterdon't send a record key and are unchanged.Tests
bin/__tests__/oidc.test.tsruns the CLI against a local server that plays both GitHub and Currents: precedence of the sources, the request shapes, retries, the audience for the default and a custom URL, revoke, masking with--debug,--json, and the missing-key hint.npm run build -w @currents/rulesbefore the tests that run the built CLI pass.🤖 Generated with Claude Code
https://claude.ai/code/session_011baD3jW7wFPFbKF1o7FCeC