Skip to content

Oauth

Category: Mcp_client

Source: oauth.dart

OAuth 2.1 Authorization Code Flow + PKCE + Dynamic Client Registration for MCP servers.

Used by glue mcp auth login <server> (and /mcp auth login).

Flow:

  1. Discover the authorization-server metadata at <base>/.well-known/oauth-authorization-server (RFC 8414).
  2. (Optional) Dynamically register a client (RFC 7591) and persist the issued client_id so we don't re-register on subsequent logins for the same server.
  3. Open the user's browser to authorization_endpoint?... with a PKCE challenge + a fresh state parameter.
  4. Bind a one-shot loopback HTTP server on 127.0.0.1:0 that the browser will redirect to. Capture code + verify state.
  5. Exchange the code at token_endpoint for an access token (+ optional refresh token).
  6. Persist the tokens to [CredentialStore] under mcp:<server-id> with the field names declared in [McpOAuthFields].

No secrets are logged. The browser URL itself is shown to the user (it contains the state + challenge but no token).

Enums

McpAuthInvalidation

Scope of an MCP auth invalidation. Mirrors the TS SDK's OAuthClientProvider.invalidateCredentials(scope) granularity so we can drop just the bit that failed without round-tripping DCR or rediscovery.

ValueDescription
client
scope
all
refresh_token
expires_at

Classes

McpAuthDiscovery

Result of [discoverMcpAuth] — everything needed to run the auth flow and cache the outcome in config.

Constructor

dart
const McpAuthDiscovery({
    required this.endpoints,
    required this.scopes,
    this.resourceMetadataUrl,
    this.authorizationServer,
  })

Properties

PropertyTypeDescription
endpointsOAuthEndpoints
scopesList<String>
resourceMetadataUrlUri?null when discovery fell back to the legacy direct path.
authorizationServerUri?null when discovery fell back to the legacy direct path.

OAuthEndpoints

Constructor

dart
const OAuthEndpoints({
    required this.authorizationEndpoint,
    required this.tokenEndpoint,
    this.registrationEndpoint,
    this.scopesSupported = const [],
  })
dart
factory OAuthEndpoints.fromMetadata(Map<String, dynamic> json)

Properties

PropertyTypeDescription
authorizationEndpointUri
tokenEndpointUri
registrationEndpointUri?
scopesSupportedList&lt;String&gt;

OAuthDiscoveryException

Constructor

dart
const OAuthDiscoveryException(this.message)

Properties

PropertyTypeDescription
messageString

Methods

String toString()

ProtectedResourceMetadata

Parsed RFC 9728 protected resource metadata document.

Constructor

dart
const ProtectedResourceMetadata({
    required this.resource,
    required this.authorizationServers,
    this.scopesSupported = const [],
    required this.metadataUrl,
  })

Properties

PropertyTypeDescription
resourceUriresource field — should equal the MCP server's origin.
authorizationServersList&lt;Uri&gt;authorization_servers — MCP requires at least one.
scopesSupportedList&lt;String&gt;scopes_supported — passed through to authorize URL when no WWW-Authenticate scope is available.
metadataUrlUriURL the metadata was fetched from — cached in config to skip rediscovery next session.
clientfinal
ownsClientfinal
pathfinal
resfinal
jsonfinal
resourceRawfinal
authServersRawfinal
authServersfinal
schemeString
parametersMap&lt;String, String&gt;
trimmedfinal
spaceIdxfinal
schemefinal
restfinal
paramsfinal
outfinal
ivar
resourceMetadataUri? getresource_metadata parameter from RFC 9728 §5.1. The MCP spec requires servers to advertise this URL on 401.
scopeList&lt;String&gt; getSpace-delimited scopes from RFC 6750 §3. Empty list when absent.

Methods

`const WwwAuthenticateChallenge({
required this.scheme,
required this.parameters,

})`

return WwwAuthenticateChallenge(scheme: 'Bearer', parameters: params)

WwwAuthenticateChallenge

Parsed WWW-Authenticate: Bearer … challenge. Only Bearer is relevant for MCP — other schemes return null from [parseWwwAuthenticate].

Constructor

dart
const WwwAuthenticateChallenge({
    required this.scheme,
    required this.parameters,
  })

Properties

PropertyTypeDescription
schemeString
parametersMap&lt;String, String&gt;
resourceMetadataUri? getresource_metadata parameter from RFC 9728 §5.1. The MCP spec requires servers to advertise this URL on 401.
scopeList&lt;String&gt; getSpace-delimited scopes from RFC 6750 §3. Empty list when absent.

OAuthClient

Constructor

dart
const OAuthClient({required this.clientId, this.clientSecret})

Properties

PropertyTypeDescription
clientIdString
clientSecretString?

OAuthRegistrationException

Constructor

dart
const OAuthRegistrationException(this.message)

Properties

PropertyTypeDescription
messageString

Methods

String toString()

OAuthTokens

Constructor

dart
const OAuthTokens({
    required this.accessToken,
    this.refreshToken,
    this.expiresAt,
    this.scope,
  })

Properties

PropertyTypeDescription
accessTokenString
refreshTokenString?
expiresAtDateTime?
scopeString?

OAuthFlowException

Constructor

dart
const OAuthFlowException(this.message)

Properties

PropertyTypeDescription
messageString

Methods

String toString()

abstract McpOAuthFields

Field names used under CredentialStore's mcp:&lt;server-id&gt; namespace for OAuth-style auth. Bearer-token-only servers use the parallel bearer field; the two flows are orthogonal.

Properties

PropertyTypeDescription
accessTokenconst String
refreshTokenconst String
expiresAtIsoconst String
clientIdconst String
clientSecretconst String
scopeconst String

Functions

`Future<OAuthEndpoints> discoverOAuthEndpoints(

Uri serverBaseUrl, { http.Client? httpClient, })`

Discovers the OAuth metadata for an MCP server. Tries the canonical RFC 8414 path (/.well-known/oauth-authorization-server); falls back to the OIDC discovery path (/.well-known/openid-configuration).

`Future<McpAuthDiscovery> discoverMcpAuth({

required Uri serverUrl, String? wwwAuthenticate, Uri? cachedResourceMetadataUrl, http.Client? httpClient, })`

MCP-spec discovery: RFC 9728 protected resource metadata → RFC 8414 authorization server metadata. Falls back to the legacy direct probe against the server URL when no RFC 9728 metadata is found.

Scopes from WWW-Authenticate win over scopes_supported in protected-resource metadata (RFC 6750 + MCP spec).

`Future<OAuthEndpoints> discoverAuthorizationServerMetadata({

required Uri authServer, http.Client? httpClient, })`

Discovers RFC 8414 authorization server metadata for [authServer]. Falls back to OIDC discovery (/.well-known/openid-configuration).

Validates the issuer field equals the URL the metadata was fetched from — required by RFC 8414 §3.3 and MCP §"After retrieving a metadata document".

Throws [OAuthDiscoveryException] when no compliant metadata is found.

`Future<ProtectedResourceMetadata> discoverProtectedResourceMetadata({

required Uri serverUrl, Uri? resourceMetadataUrl, http.Client? httpClient, })`

Discovers RFC 9728 protected resource metadata for an MCP server.

Tries, in order:

  1. [resourceMetadataUrl] when supplied (from WWW-Authenticate or cached config).
  2. &lt;server&gt;/.well-known/oauth-protected-resource{path} — RFC 9728 path-suffix form.
  3. &lt;server&gt;/.well-known/oauth-protected-resource — root form.

Throws [OAuthDiscoveryException] when nothing returns valid metadata.

WwwAuthenticateChallenge? parseWwwAuthenticate(String? header)

Parses a WWW-Authenticate header. Returns null when the header is missing, empty, or carries a non-Bearer scheme.

Handles RFC 7235 quoted-string values (which may contain commas).

`Future<OAuthClient> registerOAuthClient({

required Uri registrationEndpoint, required Uri redirectUri, required String clientName, http.Client? httpClient, })`

`Future<OAuthTokens> refreshOAuthTokens({

required OAuthEndpoints endpoints, required OAuthClient client, required String refreshToken, http.Client? httpClient, })`

Refresh-token grant. Returns new tokens; the refresh token may rotate.

`void storeMcpOAuthTokens({

required String serverId, required OAuthClient client, required OAuthTokens tokens, required CredentialStore credentials, })`

Persists [tokens] + [client] into the credential store under mcp:&lt;serverId&gt;. Idempotent — re-running the login flow overwrites.

`void clearMcpOAuthTokens({

required String serverId, required CredentialStore credentials, })`

Clears all OAuth fields for [serverId] from the credential store. Leaves the bearer field (if any) intact.

`void invalidateMcpAuth({

required String serverId, required McpAuthInvalidation scope, required CredentialStore credentials, })`

Drops [scope]'s portion of MCP OAuth credentials for [serverId]. No-op for [McpAuthInvalidation.discovery] (handled in the config writer).

`String? readMcpOAuthAccessToken({

required String serverId, required CredentialStore credentials, })`

Reads the current OAuth access token for [serverId]. Returns null when nothing is stored. Does not refresh — callers handle expiry elsewhere.

Released under the MIT License.