feat(tokens): long-lived operator session refresh tokens (90-day TTL with auto-propagation)#54
Conversation
…h BASE_URL
When BASE_URL is set, resolveJwksUrl pointed the verifier at <BASE_URL>/.well-known/jwks.json.
On Cloudflare the worker's BASE_URL is its OWN custom domain (api.relayauth.dev), so
verification made the worker fetch itself — a self-subrequest that fails ("Failed to fetch
JWKS"), throwing in verifyRs256Token and rejecting EVERY RS256 bearer as invalid_token. Only
RS256-bearer auth (e.g. the admin-bearer api-key mint) was affected; x-api-key auth never
fetches JWKS. The worker already holds RELAYAUTH_SIGNING_KEY_PEM_PUBLIC, so build the single-key
JWKS inline (a data: URL, no network) and only fall back to a network fetch when no public key
is bound.
Diagnosed live via worker logs: verifyToken threw "RelayAuthError: Failed to fetch JWKS".
…ions Allows callers to request a custom refresh token TTL (up to 90 days) on POST /v1/tokens. The TTL is stored in token meta (refreshTokenTtl) and propagated through each rotation on POST /v1/tokens/refresh, so an unattended host can hold a 30–90 day session that silently renews without interactive re-login. - POST /v1/tokens accepts refreshTokenTtlSeconds (capped at 90d) - issueTokenPair embeds the TTL in meta on both access and refresh claims - POST /v1/tokens/refresh reads meta.refreshTokenTtl and reissues with the same TTL on each rotation - IdentityTokenIssueRequest type added to @relayauth/types - 5 new tests covering TTL issuance, capping, defaulting, and propagation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Warning Review limit reached
More reviews will be available in 49 minutes and 43 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds an optional ChangesConfigurable Refresh Token TTL
JWKS URL Resolution Fix
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
… /agent endpoints Cloud's delegated-token mint calls POST /v1/tokens/path, /v1/tokens/workspace-path, and /v1/tokens/agent — not just POST /v1/tokens. Without this fix those endpoints would silently ignore refreshTokenTtlSeconds and fall back to the 24h default, breaking the relayfile unattended-refresh contract. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Cross-repo contract blocker from Cloud review: Cloud PR https://github.com/AgentWorkforce/cloud/pull/2260 sends This PR currently appears to wire Please accept/cap Note: I tried to submit this as a request-changes review, but GitHub disallowed it because the PR author account matches this reviewer account. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bff4f62dfb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| accessScopes, | ||
| accessAudience, | ||
| accessExpiresIn, | ||
| refreshTokenTtlSeconds, |
There was a problem hiding this comment.
Thread refresh TTL through delegated token endpoints
This only passes the requested refresh TTL for POST /v1/tokens; the delegated/operator endpoints still use AgentTokenRequest/PathTokenRequest and call issueTokenPair without refreshTokenTtlSeconds, so a caller following the commit contract and sending refreshTokenTtlSeconds to /v1/tokens/agent, /v1/tokens/path, or /v1/tokens/workspace-path gets a 24h refresh token with no meta.refreshTokenTtl. Those sessions then rotate with the default 24h TTL, leaving the daily re-login problem unfixed for the relayfile delegated token mint flow.
Useful? React with 👍 / 👎.
|
Re-review after commit Remaining proof gap: the new tests still appear to cover only plain Once that test is in, Cloud considers the relayauth contract aligned and proven. |
… rotation Adds the delegated-endpoint regression test requested during cross-review: mints a relay_pa via POST /v1/tokens/workspace-path with refreshTokenTtlSeconds equal to the Cloud-used 90d value, asserts meta.refreshTokenTtl is present on both minted tokens, then calls /v1/tokens/refresh and asserts the rotated pair retains the same TTL and meta. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
khaliqgant
left a comment
There was a problem hiding this comment.
Relay cross-review against AgentWorkforce/relay#1148 and AgentWorkforce/cloud#2260:
The current code path appears contract-aligned: refreshTokenTtlSeconds is now parsed/capped and forwarded for /v1/tokens, /v1/tokens/agent, /v1/tokens/path, and /v1/tokens/workspace-path, and /v1/tokens/refresh preserves the TTL from meta.refreshTokenTtl.
Remaining proof gap before full signoff: please add one delegated endpoint regression test, preferably /v1/tokens/workspace-path, that sends refreshTokenTtlSeconds: 7776000, asserts token meta includes refreshTokenTtl: "7776000", refreshes through /v1/tokens/refresh, and asserts the rotated refresh token retains the same TTL/meta. That directly covers the Cloud-used Relayfile delegated credential path.
|
Re-review after commit Cloud contract review is satisfied for relayauth PR #54. Recommended order remains relayauth -> cloud -> relay. |
|
Relay cross-review final signoff: the delegated endpoint proof gap is closed. Current diff includes a Note: GitHub would not accept a formal approval from this shared automation identity because it treats it as approving its own PR, so recording signoff as a comment. |
|
Addressing Codex bot review (discussion_r3430966670): The P1 finding —
The third commit ( |
Summary
refreshTokenTtlSecondsparameter toPOST /v1/tokens, capped at 90 days (MAX_OPERATOR_REFRESH_TOKEN_TTL_SECONDS)meta.refreshTokenTtlon both access and refresh claims at issuancePOST /v1/tokens/refreshreadsmeta.refreshTokenTtland reissues each rotated refresh token with the same TTL — silent indefinite renewal without interactive re-loginIdentityTokenIssueRequesttype to@relayauth/typesMotivation
Fixes the root cause described in
handoff/agent-relay-session-token-ttl.md: operator sessions had a fixed 24h refresh TTL, forcing interactiveagent-relay cloud logindaily. This is the relayauth layer of a three-repo fix (also touchesagent-relayandcloud).Contract for consumers
Cloud (relayfile delegated token mint): Pass
refreshTokenTtlSeconds: 7776000in the body ofPOST /v1/tokens(and/v1/tokens/path,/v1/tokens/agent) when minting operator/delegated sessions.Agent-relay: The
refreshTokenExpiresAtfield inTokenPairresponses is already populated — track it in the canonical auth store and trigger refresh proactively (before access token expires, with a backstop before refresh token expires).Test plan
npm run test -w packages/server→ 360/360 passtsc --noEmitclean on bothpackages/serverandpackages/typesrefreshTokenTtlSeconds→ 24h refresh TTL as before)🤖 Generated with Claude Code