Skip to content
14 changes: 14 additions & 0 deletions src/ModelContextProtocol.Core/Authentication/ClientOAuthOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ public sealed class ClientOAuthOptions
/// </remarks>
public AuthorizationRedirectDelegate? AuthorizationRedirectDelegate { get; set; }

/// <summary>
/// Gets or sets the token endpoint authentication method selector function.
/// </summary>
/// <remarks>
/// <para>
/// This function is used to select which token endpoint authentication method to use when multiple methods are available.
/// If not specified, the first available method will be selected.
/// </para>
/// <para>
/// The function receives a list of supported authentication methods from the authorization server metadata and should return the selected method, or null if no suitable method is found.
/// </para>
/// </remarks>
public Func<IReadOnlyList<string>?, string?>? TokenEndpointAuthMethodSelector { get; set; }

/// <summary>
/// Gets or sets the authorization server selector function.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ internal sealed partial class ClientOAuthProvider : McpHttpClient
private readonly Uri _redirectUri;
private readonly string? _configuredScopes;
private readonly IDictionary<string, string> _additionalAuthorizationParameters;
private readonly Func<IReadOnlyList<string>?, string?> _tokenEndpointAuthMethodSelector;
private readonly Func<IReadOnlyList<Uri>, Uri?> _authServerSelector;
private readonly AuthorizationRedirectDelegate _authorizationRedirectDelegate;
private readonly Uri? _clientMetadataDocumentUri;
Expand Down Expand Up @@ -79,6 +80,9 @@ public ClientOAuthProvider(
_additionalAuthorizationParameters = options.AdditionalAuthorizationParameters;
_clientMetadataDocumentUri = options.ClientMetadataDocumentUri;

// Set up token endpoint authentication method selector (use default if not provided)
_tokenEndpointAuthMethodSelector = options.TokenEndpointAuthMethodSelector ?? DefaultTokenEndpointAuthMethodSelector;

// Set up authorization server selection strategy
_authServerSelector = options.AuthServerSelector ?? DefaultAuthServerSelector;

Expand All @@ -92,6 +96,13 @@ public ClientOAuthProvider(
_tokenCache = options.TokenCache ?? new InMemoryTokenCache();
}

/// <summary>
/// Default token endpoint authentication method selector that selects the first supported method from the authorization server metadata.
/// </summary>
/// <param name="tokenEndpointAuthMethodsSupported">The list of supported token endpoint authentication methods.</param>
/// <returns>The selected token endpoint authentication method, or null if none are available.</returns>
private static string? DefaultTokenEndpointAuthMethodSelector(IReadOnlyList<string>? tokenEndpointAuthMethodsSupported) => tokenEndpointAuthMethodsSupported?.FirstOrDefault();

/// <summary>
/// Default authorization server selection strategy that selects the first available server.
/// </summary>
Expand Down Expand Up @@ -306,7 +317,7 @@ await _tokenCache.GetTokensAsync(cancellationToken).ConfigureAwait(false) is { R
}

// Determine the token endpoint auth method from server metadata if not already set by DCR.
_tokenEndpointAuthMethod ??= authServerMetadata.TokenEndpointAuthMethodsSupported?.FirstOrDefault();
_tokenEndpointAuthMethod ??= _tokenEndpointAuthMethodSelector(authServerMetadata.TokenEndpointAuthMethodsSupported);

// Store auth server metadata for future refresh operations
_authServerMetadata = authServerMetadata;
Expand Down