-
Notifications
You must be signed in to change notification settings - Fork 1.3k
SEP-2207: OIDC-flavored refresh token guidance #2207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b5fd83b
eda2f5e
8c1e359
b0fd8c6
6246047
6b07715
bc71309
dbe4f7d
8d1060c
60966c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,229 @@ | ||
| --- | ||
| title: "SEP-2207: OIDC-Flavored Refresh Token Guidance" | ||
| sidebarTitle: "SEP-2207: OIDC-Flavored Refresh Token Guidance" | ||
| description: "OIDC-Flavored Refresh Token Guidance" | ||
| --- | ||
|
|
||
| <div className="flex items-center gap-2 mb-4"> | ||
| <Badge color="gray" shape="pill"> | ||
| Draft | ||
| </Badge> | ||
| <Badge color="gray" shape="pill"> | ||
| Standards Track | ||
| </Badge> | ||
| </div> | ||
|
|
||
| | Field | Value | | ||
| | ------------- | ------------------------------------------------------------------------------- | | ||
| | **SEP** | 2207 | | ||
| | **Title** | OIDC-Flavored Refresh Token Guidance | | ||
| | **Status** | Draft | | ||
| | **Type** | Standards Track | | ||
| | **Created** | 2026-02-04 | | ||
| | **Author(s)** | Wils Dawson ([@wdawson](https://github.com/wdawson)) | | ||
| | **Sponsor** | Paul Carleton ([@pcarleton](https://github.com/pcarleton)) | | ||
| | **PR** | [#2207](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2207) | | ||
|
|
||
| --- | ||
|
|
||
| ## Abstract | ||
|
|
||
| This proposal provides guidance for MCP implementations regarding refresh token | ||
| issuance and requests, particularly when Authorization Servers support the | ||
| `offline_access` scope. The `offline_access` scope originated in OIDC but can be | ||
| adopted by any OAuth 2.1 Authorization Server as a mechanism to let clients | ||
| explicitly request refresh tokens. This SEP clarifies the expected behavior for | ||
| both Authorization Servers and MCP Clients when working with this pattern. | ||
|
|
||
| ## Motivation | ||
|
|
||
| MCP's authorization mechanism is based on OAuth 2.1, but many real-world | ||
| deployments use Authorization Servers that also implement OpenID Connect (OIDC). | ||
| A key difference between pure OAuth and OIDC is how refresh tokens are handled: | ||
|
|
||
| - In **pure OAuth 2.1**, there is no standard mechanism for a client to | ||
| explicitly request a refresh token. The Authorization Server determines | ||
| whether to issue one based on the client's capabilities (e.g., the | ||
| `refresh_token` grant type in client metadata) and its own policies. | ||
| - In **OIDC** (and Authorization Servers that adopt this convention), the | ||
| `offline_access` scope exists to allow clients to explicitly request refresh | ||
| tokens, in addition to the OAuth logic. | ||
|
|
||
| This creates several problems in the MCP ecosystem: | ||
|
|
||
| 1. **Clients aren't requesting refresh tokens**: Major MCP clients (Cursor, | ||
| Claude, VS Code, etc.) aren't explicitly asking for refresh tokens via the | ||
| `offline_access` scope because they don't know whether the Authorization | ||
| Server supports, expects, or requires it. | ||
|
|
||
| 2. **Resource servers shouldn't specify `offline_access`**: The `offline_access` | ||
| scope is not a resource-specific scope—it's a concern between the client and | ||
| Authorization Server. Including it in the `WWW-Authenticate` header's `scope` | ||
| parameter or in the Protected Resource Metadata's `scopes_supported` would be | ||
| semantically incorrect since it implies the resource _requires_ refresh | ||
| tokens, which it never would. | ||
|
|
||
| 3. **Authorization Servers need guidance**: When processing an authorization | ||
| code grant, Authorization Servers need clear guidance on when to issue | ||
| refresh tokens, especially when the client hasn't explicitly requested | ||
| `offline_access`. | ||
|
|
||
| 4. **Interoperability gap**: Without this guidance, implementations may behave | ||
| inconsistently, leading to poor user experience (frequent re-authentication) | ||
| or security issues (issuing refresh tokens to clients that can't securely | ||
| store them). | ||
|
|
||
| ## Specification | ||
|
|
||
| ### Authorization Server Guidelines | ||
|
|
||
| 1. **Client capability check**: The Authorization Server **SHOULD** check the | ||
| client metadata for `refresh_token` in the `grant_types` field. If the client | ||
| does not advertise support for the `refresh_token` grant, the Authorization | ||
| Server **SHOULD NOT** issue a refresh token. | ||
|
|
||
| 2. **Risk-based assessment**: The Authorization Server **SHOULD** determine | ||
| based on its own risk assessment whether to issue a refresh token to clients | ||
| that support them. This enables security policies such as: | ||
| - Not issuing refresh tokens to newly-registered client domains until | ||
| reputation is established | ||
| - Requiring additional verification for high-risk clients | ||
| - Implementing domain allowlists for refresh token issuance | ||
|
|
||
| 3. **`offline_access` scope handling**: If the client requests the | ||
| `offline_access` scope, the Authorization Server **MAY** treat this as | ||
| equivalent to the client advertising `refresh_token` grant support in its | ||
| client metadata. However, the risk-based assessment (point 2) still applies— | ||
| requesting `offline_access` does not guarantee a refresh token will be | ||
| issued. | ||
|
|
||
| ### MCP Client Requirements | ||
|
|
||
| MCP Clients that intend to use refresh tokens and are capable of storing them | ||
| securely **SHOULD** follow these guidelines: | ||
|
|
||
| 1. **Advertise capability**: Clients **SHOULD** include `refresh_token` in their | ||
| `grant_types` client metadata to indicate they support refresh tokens. | ||
|
|
||
| 2. **Scope augmentation**: When the client desires a refresh token and the | ||
| Authorization Server metadata contains `offline_access` in its | ||
| `scopes_supported` field, the client **MAY** add the `offline_access` scope | ||
| to the list of scopes from the resource server before making authorization | ||
| requests to the Authorization Server. | ||
|
|
||
| 3. **No guarantee**: Clients **MUST NOT** assume that advertising support or | ||
| requesting `offline_access` guarantees they will receive a refresh token. The | ||
| Authorization Server retains discretion based on its policies. | ||
|
|
||
| ### MCP Server (Resource Server) Requirements | ||
|
|
||
| MCP Servers (acting as OAuth 2.0 Protected Resources): | ||
|
|
||
| 1. **SHOULD NOT** include `offline_access` in the `scope` parameter of | ||
| `WWW-Authenticate` headers, as refresh tokens are not a resource requirement. | ||
|
|
||
| 2. **SHOULD NOT** include `offline_access` in `scopes_supported` in Protected | ||
| Resource Metadata, as it is not a resource-specific scope. | ||
|
|
||
| ## Rationale | ||
|
|
||
| ### Why not require `offline_access` in the 401 response? | ||
|
|
||
| The `offline_access` scope is fundamentally different from resource-specific | ||
| scopes. It represents a client's desire for long-lived access, not a | ||
| requirement of the resource. Per | ||
| [OAuth 2.1 Section 5.3.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-5.3.1), | ||
| the `scope` attribute in `WWW-Authenticate` indicates "the required scope of the | ||
| access token for accessing the requested resource." Since the resource doesn't | ||
| require `offline_access`, including it would be semantically incorrect. | ||
|
|
||
| ### Why check client metadata for grant types? Why not always issue refresh tokens? | ||
|
|
||
| OAuth 2.1 requires clients to register their supported grant types. A client | ||
| that doesn't support the `refresh_token` grant either: | ||
|
|
||
| - Cannot securely store refresh tokens | ||
| - Has no mechanism to use them | ||
|
|
||
| Issuing refresh tokens to such clients wastes Authorization Server resources | ||
| (tracking tokens that will never be used) and may pose security risks if the | ||
| tokens are leaked. | ||
|
|
||
| ### Why allow `offline_access` as an alternative signal? | ||
|
|
||
| Some Authorization Servers—whether fully OIDC-compliant or simply adopting this | ||
| convention—only issue refresh tokens when `offline_access` is explicitly | ||
| requested. Supporting this pattern provides a compatible path for such | ||
| deployments. Clients can detect Authorization Servers that support this | ||
| convention by checking for `offline_access` in `scopes_supported` in the | ||
| Authorization Server Metadata and adapt their behavior accordingly. | ||
|
|
||
| ### Alternative approaches considered | ||
|
|
||
| 1. **Mandate `offline_access` in resource responses**: Rejected because it | ||
| misrepresents the resource's requirements and creates an anti-pattern. | ||
|
|
||
| 2. **Always issue refresh tokens**: Rejected because it ignores client | ||
| capabilities and Authorization Server security policies. | ||
|
|
||
| 3. **Separate OIDC-specific specification**: Rejected in favor of a unified | ||
| approach that works for both pure OAuth and OIDC deployments. | ||
|
|
||
| ## Backward Compatibility | ||
|
|
||
| This proposal is fully backward-compatible: | ||
|
|
||
| - Clients that already request `offline_access` continue to work | ||
| - Authorization Servers that already check client capabilities continue to work | ||
| - MCP Servers are not required to make any changes | ||
| - The guidance is additive and does not change existing required behavior | ||
|
|
||
| Implementations that don't follow this guidance may experience suboptimal | ||
| behavior (missing refresh tokens or unnecessary token issuance) but will remain | ||
| functional. | ||
|
|
||
| ## Security Implications | ||
|
|
||
| ### Positive security implications | ||
|
|
||
| 1. **Reduced token leakage risk**: By not issuing refresh tokens to clients that | ||
| don't advertise support, we reduce the risk of long-lived tokens being stored | ||
| insecurely. | ||
|
|
||
| 2. **Defense in depth**: The risk-based assessment step gives Authorization Servers | ||
| flexibility to implement additional security controls. | ||
|
|
||
| ### Considerations | ||
|
|
||
| 1. **Client metadata may not be sufficient**: Since client metadata is | ||
| self-reported, a malicious actor could register a client claiming | ||
| `refresh_token` grant support to obtain long-lived tokens. Authorization | ||
| Servers MAY use the risk-based assessment step (see Specification) to apply | ||
| additional restrictions—such as domain allowlists, reputation checks, or | ||
| verification requirements—rather than solely relying on client metadata | ||
| claims when deciding whether to issue refresh tokens. | ||
|
|
||
| 2. **Scope injection**: Clients adding `offline_access` should ensure this | ||
| doesn't interfere with other scope-related logic or create unexpected | ||
| authorization prompts. | ||
|
|
||
| ## Reference Implementation | ||
|
|
||
| Reference implementations demonstrating this guidance will be provided in the | ||
| official MCP SDKs: | ||
|
|
||
| - **TypeScript SDK**: Client-side `offline_access` scope handling | ||
| - **Python SDK**: Client-side `offline_access` scope handling | ||
| - **Authorization Server example**: Demonstration of client capability checking | ||
wdawson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - **Client conformance test**: Allowing for easy validation of SDK implementations | ||
|
|
||
| Links to implementations will be added once the SEP is accepted. | ||
|
|
||
| ## Acknowledgments | ||
|
|
||
| This proposal was developed through discussion in the MCP Discord's | ||
| authorization channel, with input from: | ||
|
|
||
| - Aaron Parecki (OAuth/OIDC expertise) | ||
| - Paul Carleton (MCP authorization guidance) | ||
| - Simon Russell (OIDC deployment experience) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -484,6 +484,32 @@ own resources. | |
|
|
||
| MCP servers **MUST NOT** accept or transit any other tokens. | ||
|
|
||
| ## Refresh Tokens | ||
|
|
||
| This section provides guidance for MCP Clients and MCP Servers when handling or issuing | ||
| refresh tokens for both OAuth and OpenID Connect. | ||
|
|
||
| **MCP Clients** that desire refresh tokens: | ||
|
|
||
| - **MUST** keep refresh tokens confidential in transit and storage as specified in [OAuth 2.1 Section 4.3](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-14#section-4.3) | ||
| - **SHOULD** include `refresh_token` in their `grant_types` client metadata | ||
wdawson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - **MAY** add `offline_access` to the `scope` parameter of the authorization and token requests when the Authorization Server metadata contains it in `scopes_supported` | ||
| - **MUST NOT** assume refresh tokens will be issued; the AS retains discretion | ||
|
|
||
| **MCP Servers** (Protected Resources) **SHOULD NOT** include `offline_access` in | ||
| `WWW-Authenticate` scope or Protected Resource Metadata `scopes_supported`, as refresh | ||
| tokens are not a resource requirement. | ||
|
|
||
| When building or selecting an Authorization Server for use in protecting MCP Servers, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I addressed your feedback here. I agree it's a bit odd to put this here, but didn't see this specific guidance mostly for the first bullet point in the OAuth 2.1 document and wanted to make sure it was covered somewhere. I may have missed it. @aaronpk might speak to it better, but I'm happy to remove this AS guidance section entirely if we think it's covered sufficiently in other specs. |
||
| the Authorization Server: | ||
|
|
||
| - **SHOULD** check client metadata for `refresh_token` in `grant_types` and **SHOULD NOT** issue | ||
| refresh tokens to clients that don't advertise support | ||
| - **SHOULD** determine based on risk assessment whether to issue refresh tokens (e.g., reputation-based policies) | ||
| whether to issue refresh tokens to a client that supports them | ||
| - **MAY** treat `offline_access` scope requests as equivalent to `refresh_token` grant support, | ||
| though risk-based policies still apply. | ||
|
|
||
| ## Error Handling | ||
|
|
||
| Servers **MUST** return appropriate HTTP status codes for authorization errors: | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.