Skip to content

fix(auth): bump MCP OAuth access token TTL to 7d#4365

Merged
saddlepaddle merged 1 commit into
mainfrom
mcp-auth-expiry-issue
May 10, 2026
Merged

fix(auth): bump MCP OAuth access token TTL to 7d#4365
saddlepaddle merged 1 commit into
mainfrom
mcp-auth-expiry-issue

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented May 10, 2026

Summary

  • oauthProvider was using the better-auth default accessTokenExpiresIn of 1h. MCP clients that don't request offline_access (or whose refresh-token storage flakes) end up re-auth'ing multiple times a day. Set it to 7d.

Background

This is the same problem we fixed client-side in #4069 for the Superset CLI ("the 1h access-token TTL plus no refresh path meant the CLI forced re-login multiple times per day"). The CLI fix added offline_access + a refresh handler, but for third-party MCP clients (Claude Desktop, Cursor, ChatGPT Connectors, etc.) we can't dictate scopes — we have to make the server tolerant.

7d was picked to cover a typical work week so a single refresh hiccup doesn't bounce someone out, while keeping the blast radius of a leaked token finite.

Why not "fix it properly" upstream

Two real issues live in @better-auth/oauth-provider and are still present in latest (1.6.10) and 1.7.0-beta.3:

  1. Refresh-token grant doesn't preserve audience (RFC 8707 §2.2 says it SHOULD). checkResource reads ctx.body.resource fresh on every request — if a client refreshes without sending resource, the issued token is opaque, and our MCP route only verifies JWTs. Our route → 401 → forced re-auth.
  2. Refresh-token reuse detection is aggressive: a single replay of a rotated refresh token deleteMany's every refresh token for that user+client.

The MCP TS SDK does pass resource on refresh so (1) doesn't bite SDK-using clients today, and (2) is hard to relax without security tradeoffs. Not changing better-auth versions in this PR — pinning 1.6.5 across the board for now.

Test plan

  • Deploy and confirm a fresh MCP login from Claude Code / Cursor stays authenticated past 1h
  • Existing tokens in the wild still expire on their original 1h schedule — verify by inspecting oauth_access_tokens.expires_at for tokens issued before vs. after deploy
  • No regression in CLI auth (CLI already refreshes proactively, but it should now refresh less often)

Summary by cubic

Increase MCP OAuth access token TTL from 1 hour to 7 days to prevent frequent re-auth for clients without offline_access or flaky refresh storage. Makes the server more tolerant of third‑party MCP clients while keeping risk bounded.

  • Bug Fixes
    • Set accessTokenExpiresIn to 7 days in the OAuth provider.
    • Existing tokens keep their 1h expiry; new tokens get 7d.
    • No dependency changes; known @better-auth/oauth-provider refresh-audience issue remains.

Written for commit 679f658. Summary will update on new commits.

Summary by CodeRabbit

  • Chores
    • Updated OAuth access token expiration to 7 days

Review Change Stack

The `oauthProvider` was using the default `accessTokenExpiresIn` of 1h.
For MCP clients that don't request `offline_access` (or whose refresh
token storage is flaky), this forces re-auth multiple times a day.
Bump to 7d so a refresh hiccup at most loses a week of session, not
a single hour. Same approach we took for the CLI in #4069 — but on
the server side so it covers all MCP clients, including ones we don't
control (Claude Desktop, Cursor, etc).

Note: the underlying upstream issue — `@better-auth/oauth-provider`
not preserving audience across refresh-token grants per RFC 8707 §2.2
— is still present in 1.6.10 and 1.7.0-beta.3. Worth filing separately.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3b05583f-ed09-47bf-8994-25c0812f1b2d

📥 Commits

Reviewing files that changed from the base of the PR and between 70e53ad and 679f658.

📒 Files selected for processing (1)
  • packages/auth/src/server.ts

📝 Walkthrough

Walkthrough

The OAuth provider configuration in the Better Auth setup is updated to explicitly set access token expiration to 7 days instead of relying on the prior default value.

Changes

OAuth Token Expiration Configuration

Layer / File(s) Summary
Token Expiration Configuration
packages/auth/src/server.ts
OAuth provider accessTokenExpiresIn is set to 7 days (60 * 60 * 24 * 7 seconds).

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Poem

A rabbit hops in, with tokens so bright,
Seven days now, they'll shine with delight,
Expiration's set, no guessing the time,
Configuration's clear—a very fine line! 🐰⏰

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: bumping the MCP OAuth access token TTL from the default to 7 days, which directly aligns with the changeset's objective.
Description check ✅ Passed The PR description comprehensively covers the required sections: clear summary of changes, related background context, type of change (bug fix), detailed rationale, and a test plan with specific verification steps.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mcp-auth-expiry-issue

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 10, 2026

Greptile Summary

This PR extends the OAuth access token TTL from the better-auth default of 1 hour to 7 days for the oauthProvider plugin, addressing repeated forced re-authentication for third-party MCP clients (Claude Desktop, Cursor, etc.) that don't request offline_access and therefore receive no refresh token.

  • Single-line config change in packages/auth/src/server.ts: adds accessTokenExpiresIn: 60 * 60 * 24 * 7 (604 800 s) to the oauthProvider options. The default refreshTokenExpiresIn (30 days) is untouched, so clients that do obtain a refresh token are unaffected.
  • Tradeoff acknowledged in the PR: extending access token lifetime proportionally extends the blast radius of a leaked token from 1 hour to 7 days. Stateless JWT verification means the MCP route cannot shorten that window retroactively; token revocation will only take effect after expiry.
  • Existing tokens already in oauth_access_tokens keep their original 1-hour expiry — only newly issued tokens will carry the 7-day TTL.

Confidence Score: 4/5

Safe to merge; the change is a one-line config addition with no logic or schema changes.

Extending the access token TTL from 1 hour to 7 days is a deliberate security tradeoff: a leaked JWT now stays valid for up to 7 days because the MCP route uses stateless verification and cannot invalidate tokens mid-flight. The PR description acknowledges this explicitly, and 7 days is still well within the 30-day default refresh-token window. No other behavior is altered.

Only packages/auth/src/server.ts is changed. No migrations, no schema changes, no new code paths — just the TTL value in the oauthProvider plugin config.

Important Files Changed

Filename Overview
packages/auth/src/server.ts Adds accessTokenExpiresIn: 60 * 60 * 24 * 7 to oauthProvider config, extending access token TTL from the better-auth default of 1 h to 7 days to reduce forced re-auths for MCP clients that can't use refresh tokens.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
packages/auth/src/server.ts:205
The arithmetic is correct (604 800 s = 7 days), but a named constant or inline comment would make the intended TTL immediately obvious to future readers without mental arithmetic — especially useful if the value is ever revisited.

```suggestion
			accessTokenExpiresIn: 60 * 60 * 24 * 7, // 7 days
```

Reviews (1): Last reviewed commit: "fix(auth): bump MCP OAuth access token T..." | Re-trigger Greptile

consentPage: `${env.NEXT_PUBLIC_WEB_URL}/oauth/consent`,
allowDynamicClientRegistration: true,
allowUnauthenticatedClientRegistration: true,
accessTokenExpiresIn: 60 * 60 * 24 * 7,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The arithmetic is correct (604 800 s = 7 days), but a named constant or inline comment would make the intended TTL immediately obvious to future readers without mental arithmetic — especially useful if the value is ever revisited.

Suggested change
accessTokenExpiresIn: 60 * 60 * 24 * 7,
accessTokenExpiresIn: 60 * 60 * 24 * 7, // 7 days
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/auth/src/server.ts
Line: 205

Comment:
The arithmetic is correct (604 800 s = 7 days), but a named constant or inline comment would make the intended TTL immediately obvious to future readers without mental arithmetic — especially useful if the value is ever revisited.

```suggestion
			accessTokenExpiresIn: 60 * 60 * 24 * 7, // 7 days
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 10, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

@saddlepaddle saddlepaddle merged commit c2c7eb1 into main May 10, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant