Repository navigation
feat: share one Ory login session between S3 credentials and the source.coop API - #23
Merged
Merged
Conversation
`login` now asks Ory for an access token meant for the source.coop API (`--audience`, default the site URL) and caches the whole session — refresh, ID and access tokens — once, rather than a refresh token per role. `creds` mints for any role by exchanging the session's ID token at `/.sts`, and `source-coop auth token` prints the access token, so the same login works for S3 and for the API. The refresh token lives only in the session, since it rotates on use and a replayed one can revoke the whole family: everything refreshes through `session::token`, under one lock, and one refresh renews both the ID and the access token. A role's cache keeps only the STS settings `login` was given. Caches from earlier versions, with a refresh token of the role's own, still refresh that way until the next `login`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017poyHYEyxWEJct9X8Q71qF
alukach
added this pull request to stack #25
October 9, 2026 19:20
alukach
marked this pull request as ready for review
October 9, 2026 19:37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #21. This is the auth groundwork for the gh-style commands: one
loginnow works for both S3 and the source.coop API.loginasks Ory for an access token whose audience is the source.coop site (--audience/SOURCE_API_AUDIENCE, defaulthttps://source.coop, orhttps://staging.source.coopin staging builds). It caches the whole Ory session (refresh, ID and access tokens) once, under@sessionin the keyring orsource-coop/session.json, rather than a refresh token per role.credsmints for any role, including one never logged into, by exchanging the session's ID token at/.sts. The newsource-coop auth tokenprints the access token, refreshed if it has expired.The refresh token lives only in the session. It rotates on use, and a replayed one can revoke the whole token family, so every role and every API call refreshes through one function under one lock, and one refresh renews both tokens. A role's cache keeps only its STS settings. Caches written by 0.3.0 still refresh with their own token until the next
login, so nobody has to log in again on upgrade. 0.3.0 reading a cache written by this version serves the credentials until they expire, then says to log in.Deploying: the CLI's Ory clients must allow the site as an audience and use the
jwtaccess-token strategy before this is released, orloginis refused. See the runbook below, steps 1, 2 and 6.Testing
cargo fmt --check,cargo clippy --all-targets -- -D warningsandcargo test(28 passed) all pass./.stsand the API, with an expired session cached:auth tokenand API calls sent the new access token.credsminted for a role with nothing cached, using the ID token that same refresh brought.Deployment runbook (all four PRs)
Staging goes first at every step, and production only after staging checks out. Nothing here is hard to undo; each phase lists its rollback.
0. Get the branches onto GitHub
Done when you're reading this:
open-prs.shopened these four drafts, linked them to each other, and put the source.coop PR's number into ADR-005's note.1. Configure the CLI's Ory OAuth2 clients (staging, then production)
The CLI's clients are
a79c9537-be78-454a-9ea1-b96a1be811cc(staging) and197e20e7-d52d-4d1d-9e54-4b73a342034b(production). Change only these clients, not the project and not the web app's client.ory get oauth2-client <client-id> --project <project> --format json > cli-client-before.jsonory update oauth2-clientreplaces the whole client and drops any field you leave out. Check the flags withory patch oauth2-client --helpfor your CLI version:https://staging.source.coopon the staging client,https://source.coopon the production client.access_token_strategytojwt.offline_accessis among its scopes andrefresh_tokenamong its grant types. 0.3.0's refresh already relies on both, so they should be there.source-coop loginandsource-coop credswith the installed 0.3.0. It never asks for an audience, and its ID tokens are unaffected by the access-token strategy.Rollback: restore
cli-client-before.jsonwithory update oauth2-client <client-id> --file cli-client-before.json. A full replace is what you want here.2. Read a real token's issuer (staging)
The API matches
issexactly, with or without a trailing slash, so check what Ory actually puts there before deploying the API change.git checkout feat/login-session && cargo build --features staging.jwtstrategy from step 1 isn't in effect.audcontainshttps://staging.source.coop. If it doesn't, the consent step didn't grant the audience.issequals staging'sNEXT_PUBLIC_ORY_SDK_URL, with or without a trailing slash. If it doesn't, setORY_OAUTH2_ISSUERto the token'sissin Vercel's staging environment.3. Deploy the API change to staging
feat/api-ory-access-tokens). Merging tomaindeploys staging.curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer <an ID token>" https://staging.source.coop/api/v1/whoamiThis should not resolve to your account.
Rollback: revert the merge. Ory-token calls then get 401, and nothing else changes: the proxy, the web app and S3 credentials don't use this path.
4. Check the product commands on staging
These need source.coop#651 deployed to staging, and #651 needs data.source.coop#253 deployed first (#651's own deploy-order note).
git checkout feat/product-commands && cargo build --features stagingcredsafter the cached credentials expire. It should refresh without a browser.5. Production
cargo build, no features), as in step 2. SetORY_OAUTH2_ISSUERin Vercel's production environment ifissdiffers.https://source.coop.6. Release the CLI
run_credsand the cache. Whichever merges second rebases. Whenfeat/login-sessionrebases on feat: exchange a service account's API key without a browser #20, minting from the session should take feat: exchange a service account's API key without a browser #20's early-refresh rule (16 minutes left, or half a shorter session).feat/login-session, thenfeat/product-commands. GitHub retargets the second tomainonce the first's branch is deleted.feat:commits make it 0.4.0. Merging it tags the release, and cargo-dist builds the installers and publishes the Homebrew formula.brew upgrade source-coopor the installer script) and runsource-coop login,source-coop auth token,source-coop api whoamiandsource-coop product list.product createandsource-coop api(not part of these PRs).Rollback: the previous release still works against everything above. Users on the new release who hit login trouble can pass
--audience '', which logs in as 0.3.0 did, without API commands.Notes
--audience <preview origin> --api-url <preview origin>.Related
🤖 Generated with Claude Code
https://claude.ai/code/session_017poyHYEyxWEJct9X8Q71qF