@@ -15,18 +15,40 @@ internal class DefaultAuthorizationHandler : IAuthorizationHandler
1515 private readonly ILogger _logger ;
1616 private readonly SynchronizedValue < AuthorizationContext > _authContext = new ( new AuthorizationContext ( ) ) ;
1717 private readonly Func < ClientMetadata , Task < ( string RedirectUri , string Code ) > > ? _authorizeCallback ;
18+ private readonly string ? _clientId ;
19+ private readonly string ? _clientSecret ;
20+ private readonly ICollection < string > ? _redirectUris ;
21+ private readonly ICollection < string > ? _scopes ;
1822
1923 /// <summary>
2024 /// Initializes a new instance of the <see cref="DefaultAuthorizationHandler"/> class.
2125 /// </summary>
2226 /// <param name="loggerFactory">The logger factory.</param>
23- /// <param name="authorizeCallback">A callback function that handles the authorization code flow .</param>
24- public DefaultAuthorizationHandler ( ILoggerFactory ? loggerFactory = null , Func < ClientMetadata , Task < ( string RedirectUri , string Code ) > > ? authorizeCallback = null )
27+ /// <param name="options">The authorization options .</param>
28+ public DefaultAuthorizationHandler ( ILoggerFactory ? loggerFactory = null , McpAuthorizationOptions ? options = null )
2529 {
2630 _logger = loggerFactory != null
2731 ? loggerFactory . CreateLogger < DefaultAuthorizationHandler > ( )
2832 : NullLogger < DefaultAuthorizationHandler > . Instance ;
29- _authorizeCallback = authorizeCallback ;
33+
34+ if ( options != null )
35+ {
36+ _authorizeCallback = options . AuthorizeCallback ;
37+ _clientId = options . ClientId ;
38+ _clientSecret = options . ClientSecret ;
39+ _redirectUris = options . RedirectUris ;
40+ _scopes = options . Scopes ;
41+ }
42+ }
43+
44+ /// <summary>
45+ /// Initializes a new instance of the <see cref="DefaultAuthorizationHandler"/> class.
46+ /// </summary>
47+ /// <param name="loggerFactory">The logger factory.</param>
48+ /// <param name="authorizeCallback">A callback function that handles the authorization code flow.</param>
49+ public DefaultAuthorizationHandler ( ILoggerFactory ? loggerFactory = null , Func < ClientMetadata , Task < ( string RedirectUri , string Code ) > > ? authorizeCallback = null )
50+ : this ( loggerFactory , new McpAuthorizationOptions { AuthorizeCallback = authorizeCallback } )
51+ {
3052 }
3153
3254 /// <inheritdoc />
@@ -110,26 +132,43 @@ public async Task<bool> HandleUnauthorizedResponseAsync(HttpResponseMessage resp
110132 _logger . LogDebug ( "Successfully retrieved authorization server metadata" ) ;
111133
112134 // Create client metadata
135+ string [ ] redirectUris = _redirectUris ? . ToArray ( ) ?? new [ ] { "http://localhost:8888/callback" } ;
113136 var clientMetadata = new ClientMetadata
114137 {
115- RedirectUris = new [ ] { "http://localhost:8888/callback" } , // Default redirect URI
138+ RedirectUris = redirectUris ,
116139 ClientName = "MCP C# SDK Client" ,
117- Scope = string . Join ( " " , resourceMetadata . ScopesSupported ?? Array . Empty < string > ( ) )
140+ Scope = string . Join ( " " , _scopes ?? resourceMetadata . ScopesSupported ?? Array . Empty < string > ( ) )
118141 } ;
119-
120- // Register client if the server supports it
121- if ( authServerMetadata . RegistrationEndpoint != null )
142+
143+ // Register client if needed, or use pre-configured client ID
144+ if ( ! string . IsNullOrEmpty ( _clientId ) )
145+ {
146+ _logger . LogDebug ( "Using pre-configured client ID: {ClientId}" , _clientId ) ;
147+
148+ // Create a client registration response to store in the context
149+ var clientRegistration = new ClientRegistrationResponse
150+ {
151+ ClientId = _clientId ! , // Using null-forgiving operator since we've already checked it's not null
152+ ClientSecret = _clientSecret ,
153+ } ;
154+
155+ authContext . Value . ClientRegistration = clientRegistration ;
156+ }
157+ else if ( authServerMetadata . RegistrationEndpoint != null )
122158 {
159+ // Register client dynamically
123160 _logger . LogDebug ( "Registering client with authorization server" ) ;
124161 var clientRegistration = await AuthorizationService . RegisterClientAsync ( authServerMetadata , clientMetadata ) ;
125162 authContext . Value . ClientRegistration = clientRegistration ;
126163 _logger . LogDebug ( "Client registered successfully with ID: {ClientId}" , clientRegistration . ClientId ) ;
127164 }
128165 else
129166 {
130- _logger . LogWarning ( "Authorization server does not support dynamic client registration" ) ;
167+ _logger . LogWarning ( "Authorization server does not support dynamic client registration and no client ID was provided " ) ;
131168
132- var exception = new McpAuthorizationException ( "Authorization server does not support dynamic client registration" ) ;
169+ var exception = new McpAuthorizationException (
170+ "Authorization server does not support dynamic client registration and no client ID was provided. " +
171+ "Use McpAuthorizationOptions.ClientId to provide a pre-registered client ID." ) ;
133172 exception . ResourceUri = resourceMetadata . Resource ;
134173 exception . AuthorizationServerUri = authServerUrl ;
135174 throw exception ;
@@ -142,7 +181,7 @@ public async Task<bool> HandleUnauthorizedResponseAsync(HttpResponseMessage resp
142181
143182 var exception = new McpAuthorizationException (
144183 "Authentication is required but no authorization callback was provided. " +
145- "Use SseClientTransportOptions .AuthorizeCallback to provide a callback function." ) ;
184+ "Use McpAuthorizationOptions .AuthorizeCallback to provide a callback function." ) ;
146185 exception . ResourceUri = resourceMetadata . Resource ;
147186 exception . AuthorizationServerUri = authServerUrl ;
148187 throw exception ;
@@ -155,18 +194,18 @@ public async Task<bool> HandleUnauthorizedResponseAsync(HttpResponseMessage resp
155194 // Initiate authorization code flow
156195 _logger . LogDebug ( "Initiating authorization code flow" ) ;
157196
158- // Get the registered client ID
159- var clientId = authContext . Value . ClientRegistration ! . ClientId ;
160-
161197 // Get the authorization URL that the user needs to visit
162198 var authUrl = AuthorizationService . CreateAuthorizationUrl (
163199 authServerMetadata ,
164- clientId ,
200+ authContext . Value . ClientRegistration . ClientId ,
165201 clientMetadata . RedirectUris [ 0 ] ,
166202 codeChallenge ,
167- resourceMetadata . ScopesSupported ) ;
203+ _scopes ? . ToArray ( ) ?? resourceMetadata . ScopesSupported ) ;
168204
169205 _logger . LogDebug ( "Authorization URL: {AuthUrl}" , authUrl ) ;
206+
207+ // Set the authorization URL in the client metadata
208+ clientMetadata . ClientUri = authUrl ;
170209
171210 // Let the callback handle the user authorization
172211 var ( redirectUri , code ) = await _authorizeCallback ( clientMetadata ) ;
@@ -176,7 +215,7 @@ public async Task<bool> HandleUnauthorizedResponseAsync(HttpResponseMessage resp
176215 _logger . LogDebug ( "Exchanging authorization code for tokens" ) ;
177216 var tokenResponse = await AuthorizationService . ExchangeCodeForTokensAsync (
178217 authServerMetadata ,
179- clientId ,
218+ authContext . Value . ClientRegistration . ClientId ,
180219 authContext . Value . ClientRegistration . ClientSecret ,
181220 redirectUri ,
182221 code ,
0 commit comments