Skip to content

Conversation

jba
Copy link
Contributor

@jba jba commented Jul 25, 2025

This is a preliminary implementation of OAuth 2.1 for the client.

When a StreamableClientTransport encounters a 401 Unauthorized response
from the server, it initiates the OAuth flow described in thec
authorization section of the MCP spec
(https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization).
On success, the transport obtains an access token which it passes
to all subsequent requests.

Much remains to be done here:

  • Dynamic client registration is not implemented. Since it is optional,
    we also need another way of supplying the client ID and secret to
    this code.

  • Resource Indicators, as described in section 2.5.1 of the MCP spec.

  • There is no way for the user to provide a redirect URL.

  • All of this is unexported, so it is available only to our own
    StreamingClientTransport. We should add API so people can use it
    with their own transports.

  • And, of course, tests. We should test against fake implementations
    but also, if we can find any, real reference implementations.

@jba
Copy link
Contributor Author

jba commented Jul 25, 2025

Ignore the first two commits. They should be in separate PRs.

@jba
Copy link
Contributor Author

jba commented Jul 25, 2025

/cc @RoyceLeonD

@findleyr findleyr self-requested a review July 30, 2025 19:07
@wagnerjt
Copy link

For testing, Google supports DCR and there is an example of Spotify MCP server to include client id and client secret for the fallback here. This was used to support the vscode's client (currently in insider) at the moment to do exactly this fallback mechanism when DCR does not exist at the IDP.

@RoyceLeonD
Copy link

@jba Sorry It took so long for me to be active on this. Will take a deeper look at the code and revert back in the next day or so.

jba added 4 commits August 6, 2025 13:58
Add a package for the extensions to OAuth 2.0 required by MCP.

This first PR adds Protected Resource Metadata.
Implement the Authorization Server Metadata spec.
This is a preliminary implementation of OAuth 2.1 for the client.

When a StreamableClientTransport encounters a 401 Unauthorized response
from the server, it initiates the OAuth flow described in thec
authorization section of the MCP spec
(https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization).
On success, the transport obtains an access token which it passes
to all subsequent requests.

Much remains to be done here:

- Dynamic client registration is not implemented. Since it is optional,
  we also need another way of supplying the client ID and secret to
  this code.

- Resource Indicators, as described in section 2.5.1 of the MCP spec.

- There is no way for the user to provide a redirect URL.

- All of this is unexported, so it is available only to our own
  StreamingClientTransport. We should add API so people can use it
  with their own transports.

- And, of course, tests. We should test against fake implementations
  but also, if we can find any, real reference implementations.
mcp/auth.go Outdated
Endpoint: oauth2.Endpoint{
AuthURL: asm.AuthorizationEndpoint,
TokenURL: asm.TokenEndpoint,
// DeviceAuthURL: "",

Choose a reason for hiding this comment

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

adding auth style should be necessary depending on the server metadata
https://pkg.go.dev/golang.org/x/oauth2#section-readme:~:text=type-,AuthStyle,-%C2%B6

depending on the AS's token_endpoint_auth_methods_supported.

mcp/auth.go Outdated

// Get an access token from the auth server.
config := &oauth2.Config{
ClientID: "TODO: from registration",
Copy link

@ramongalate ramongalate Aug 22, 2025

Choose a reason for hiding this comment

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

I think you should add two options, one for passed in client ids/secrets and one for PKCE for public clients, for a more AUTH2.1 compliant solution.

mcp/auth.go Outdated
}
// TODO: try more than one?
authServer := prm.AuthorizationServers[0]
// TODO: which scopes to ask for? All of them?

Choose a reason for hiding this comment

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

I don't think we should ask for all the scopes, I think the best solution would be for the client to specify in its options which scopes it wants and validate against the supported ones, failing if not supported.

Choose a reason for hiding this comment

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

and we could default to all, if no scope is passed.

mcp/auth.go Outdated
// AuthStyle: "from auth meta?",
},
RedirectURL: "", // ???
Scopes: scopes,

Choose a reason for hiding this comment

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

If you also request openid, it needs to generate and pass a nonce and validate it in the ID Token.

@besmiralia
Copy link

Hi, is there an expected timeline for this feature/pr to be reviewed and merged?

@jba
Copy link
Contributor Author

jba commented Aug 31, 2025

@besmiralia I will make progress on it this week, but client-side auth probably won't be ready until after v1 is out.

@sword-jin
Copy link

would like to help if needed, I have much experience with oauth2 and MCP, we are waiting this feature.

@jba
Copy link
Contributor Author

jba commented Sep 2, 2025

See auth/client.go for where I'm currently at with this. Basically, we do very little for you, other than provide a hook. I'd like to see whether this is sufficient. We can certainly do more, and you can find the PRM and ASM implementations in internal/oauthex.

/cc @wagnerjt @ramongalate @sword-jin @RoyceLeonD.

@wagnerjt
Copy link

wagnerjt commented Sep 3, 2025

Heya @jba -- In my opinion, the type OAuthHandler func(context.Context, OAuthHandlerArgs) (oauth2.TokenSource, error) is the minimum for everyone to implement the rest.

However, I can also see arguments that it would be great to have another option that supports only the DCR flow (creating and registering them). At the end, it still returns the (oauth2.TokenSource, error).

@findleyr
Copy link
Contributor

findleyr commented Sep 3, 2025

I can review this, but don't have much experience with oauth2. It would be really helpful if someone could opine on whether this is necessary and sufficient. Thanks!

@wagnerjt
Copy link

wagnerjt commented Sep 3, 2025

Here is a nice writeup around DCR. If anything additional would be added it would be this flow and ignoring the rest -- since there are many different ways the OAuth handshake could occur (device authorization, oidc client id/secret flow, needing to use PKCE over authorization flow) on top of the many different encryption algorithms.

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.

8 participants