Skip to content

feat(api): accept Ory access tokens from the CLI's login - #654

Draft
alukach wants to merge 1 commit into
mainfrom
feat/api-ory-access-tokens
Draft

alukach wants to merge 1 commit into
mainfrom
feat/api-ory-access-tokens

Conversation

@alukach

@alukach alukach commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Part of #588. The CLI half is source-cooperative/source-coop-cli#23.

The API now takes an Ory access token as a bearer, alongside the assertions the data proxy signs, so the CLI can call /api/v1 as the person who logged in. getApiSession routes a bearer by its unverified iss: Ory's tokens go to the new authenticateWithOryAccessToken, and the proxy's go to the existing verifier. Neither falls back to the other or to the session cookie.

The Ory verifier checks the token against Ory's published JWKS, with the issuer (with or without a trailing slash), RS256, expiry, a 30-second tolerance, and the request's origin as audience. An ID token, which Ory issues to the client ID, never passes, and which clients may mint a token for the API is decided in Ory by the audiences each client is allowed. The subject is looked up only as a person's Ory identity. Both verifiers build the session in one place, sessionForAccount. ORY_OAUTH2_ISSUER names Ory's issuer when it isn't NEXT_PUBLIC_ORY_SDK_URL.

Deploying:

Docs:

  • ADR-005: amended in docs(adr): amend ADR-005 — the API also accepts Ory access tokens data.source.coop#254. It said the API authenticates proxy-signed tokens; it now records the second signer.
  • OpenAPI: the bearerAuth description names source-coop login and source-coop auth token.
  • public/llms.txt: checked. Its line ("a token from the CLI's login or a service account") still holds.
  • docs.source.coop: checked. No page describes calling the API with a token yet.

Testing

  • npm run type-check passes.
  • npx jest src/app/api src/lib: 535 passed across 53 suites, including new suites for the Ory verifier and for getApiSession's routing.
  • next lint on the changed files reports nothing.
  • Not run against real Ory: the client configuration doesn't exist yet.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_017poyHYEyxWEJct9X8Q71qF

The API now takes an Ory access token as a bearer, alongside the assertions the data proxy signs. `source-coop login` asks Ory for an access token whose audience is the site's origin, and `getApiSession` routes a bearer by its unverified `iss`: Ory's go to a new verifier, the proxy's to the existing one, and neither falls back to the other or to the cookie. The Ory verifier checks the token against Ory's published keys (the CLI's client uses the `jwt` access-token strategy), with the issuer, RS256, expiry and the request's origin as audience, so an ID token, which Ory issues to the client ID, never passes. The subject is looked up only as a person's Ory identity.

Both verifiers now build the session in one place, `sessionForAccount`, so a token acts with the same rights whichever way it arrived. `ORY_OAUTH2_ISSUER` names Ory's issuer when it isn't the SDK URL.

Part of #588.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017poyHYEyxWEJct9X8Q71qF
@vercel

vercel Bot commented Oct 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
source-coop-ui Ready Ready Preview Oct 9, 2026 7:14pm UTC
source-cooperative Ready Ready Preview Oct 9, 2026 7:14pm UTC

Request Review

@claude

claude Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @alukach's task in 16s —— View job


✅ No blocking issues — safe to merge.

I read the full diff. The Ory verifier pins RS256 and checks issuer, audience and expiry, with a 30s tolerance. Routing on the unverified iss only chooses a verifier and is never trusted. A rejected Ory token doesn't fall back to the proxy verifier or the cookie, and the subject is looked up only as a person's identity (fetchByOryId). Sharing sessionForAccount means both token types get identical disabled-account and membership handling. I found no correctness or security problems.

Not run: I didn't execute type-check or tests.

Simplify (ponytail)

  • src/lib/api/utils.ts (new bearer parsing): authorization.toLowerCase().startsWith("bearer ") ? authorization.slice(7) : "" repeats header parsing that authenticateWithOidcToken already does. It's small and fine for now. If a third token type appears, extract one bearerToken(req) helper.
  • src/lib/api/ory-access-token.ts (_setOryJwks): this copies the test hook from oidc.ts. It's consistent with the existing code, so keep it.

Docs

  • The description still contains unfilled template placeholders: {{CLI_LOGIN_PR}}, {{ADR_PR}} and {{CLI_PRODUCT_PR}}. The ADR-005 amendment link and the deployment runbook reference are therefore unresolvable. Replace them with the real PR links before merge.
  • Otherwise the description does what CLAUDE.md asks. It names ADR-005 (amended), llms.txt (checked, still holds) and docs.source.coop (checked, no page covers it).
    · branch feat/api-ory-access-tokens

💰 Estimated review cost: $0.15 · 0m16s · 4 turns

@alukach alukach changed the title … feat(api): accept Ory access tokens from the CLI's login Oct 9, 2026

This branch was successfully deployed

2 active deployments
Preview – source-cooperative — a5966f69 Deployed Oct 9, 2026 by vercel[bot]
Preview – source-coop-ui — a5966f69 Deployed Oct 9, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant