Repository navigation
Conversation
…supabase#2610) The OAuth 2.1 server accepted and stored the `resource` parameter but never used it: every access token had `aud: "authenticated"`, so MCP servers that validate the audience rejected them. - Store the authorized resource on the session (new `sessions.resource` column) and add it to the access token `aud` claim on code exchange and every refresh. The user's audience stays first, so `aud[0]` and Data API compatibility are unchanged. - Read `resource` from form-encoded token requests. Before, only JSON bodies were checked against the authorized resource. - Reject a refresh that asks for a resource the session was not granted. - Use the RFC 8707 `invalid_target` error code for invalid, mismatched or multiple resource values, at both endpoints. - Enforce the 2048 character limit at the authorize endpoint instead of failing on the database constraint. - Return `resource` in the authorization details so consent screens can show it, and document the parameter in openapi.yaml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0187GfsUpzmeh8pDYi3eBwEq
This branch has not been deployed
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.
What
Closes #2610.
The OAuth 2.1 server already accepts, validates and stores the
resourceparameter on/oauth/authorize, but never uses it: every access token is issued withaud: "authenticated". MCP clients (Claude, ChatGPT) sendresourceper the MCP authorization spec, and MCP servers that validate the audience reject these tokens.This PR binds access tokens to the authorized resource:
Changes
sessions.resourcecolumn) and added toaudon the code exchange and on every refresh. The user's audience stays first, soaud[0](used byrequestAud,/user, etc.) and Data API compatibility are unchanged.resourcewas only read from JSON bodies, so the existing "resource must match the authorization" check was skipped for standardapplication/x-www-form-urlencodedrequests. It is now read for both.resource, but only the one granted for the session (RFC 8707 §2.2). Anything else is rejected.resourcevalues now returninvalid_target(RFC 8707 §2) instead ofinvalid_request/invalid_grant./oauth/authorizeinstead of failing on theoauth_authorizations_resource_lengthconstraint with aserver_error.GET /oauth/authorizations/{id}returnsresource, so a consent UI can show which resource server the client is asking for.resourcedocumented inopenapi.yamlfor/oauth/authorize,/oauth/tokenand the authorization details response.Compatibility
resourceare unchanged (audstays the string"authenticated").resource = NULLand keep their current tokens on refresh.audinclaims; the hook schema already allowsstring | array.resourceper authorization is supported; several values are rejected withinvalid_targetrather than silently using the first one.Migration
20261030120000_add_resource_to_sessions.up.sqladdssessions.resource text nullwith the same 2048-character check asoauth_authorizations.resource. It is idempotent.Tests
New
internal/api/oauthserver/resource_indicator_test.go(form-encoded requests throughOAuthToken/OAuthServerAuthorize):aud, with and without repeatingresource; session stores itinvalid_target, code not consumedinvalid_targeterror=invalid_targetand preservestatego test ./internal/api/oauthserver/ ./internal/tokens/ ./internal/models/ ./internal/api/pass locally against Postgres 16 (except tests that need outbound network/DNS, which fail the same way onmasterin my environment).🤖 Generated with Claude Code
https://claude.ai/code/session_0187GfsUpzmeh8pDYi3eBwEq