Skip to content

Commit f459ca6

Browse files
committed
Avoid null dereferencing when authorization_endpoint is missing
1 parent edfa7f2 commit f459ca6

File tree

1 file changed

+24
-19
lines changed

1 file changed

+24
-19
lines changed

src/ModelContextProtocol.Core/Authentication/ClientOAuthProvider.cs

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,6 @@ private async Task PerformOAuthAuthorizationAsync(
212212
// Get auth server metadata
213213
var authServerMetadata = await GetAuthServerMetadataAsync(selectedAuthServer, cancellationToken).ConfigureAwait(false);
214214

215-
if (authServerMetadata is null)
216-
{
217-
ThrowFailedToHandleUnauthorizedResponse($"Failed to retrieve metadata for authorization server: '{selectedAuthServer}'");
218-
}
219-
220215
// Store auth server metadata for future refresh operations
221216
_authServerMetadata = authServerMetadata;
222217

@@ -238,7 +233,7 @@ private async Task PerformOAuthAuthorizationAsync(
238233
LogOAuthAuthorizationCompleted();
239234
}
240235

241-
private async Task<AuthorizationServerMetadata?> GetAuthServerMetadataAsync(Uri authServerUri, CancellationToken cancellationToken)
236+
private async Task<AuthorizationServerMetadata> GetAuthServerMetadataAsync(Uri authServerUri, CancellationToken cancellationToken)
242237
{
243238
if (!authServerUri.OriginalString.EndsWith("/"))
244239
{
@@ -249,7 +244,9 @@ private async Task PerformOAuthAuthorizationAsync(
249244
{
250245
try
251246
{
252-
var response = await _httpClient.GetAsync(new Uri(authServerUri, path), cancellationToken).ConfigureAwait(false);
247+
var wellKnownEndpoint = new Uri(authServerUri, path);
248+
249+
var response = await _httpClient.GetAsync(wellKnownEndpoint, cancellationToken).ConfigureAwait(false);
253250
if (!response.IsSuccessStatusCode)
254251
{
255252
continue;
@@ -258,23 +255,36 @@ private async Task PerformOAuthAuthorizationAsync(
258255
using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
259256
var metadata = await JsonSerializer.DeserializeAsync(stream, McpJsonUtilities.JsonContext.Default.AuthorizationServerMetadata, cancellationToken).ConfigureAwait(false);
260257

261-
if (metadata != null)
258+
if (metadata is null)
262259
{
263-
metadata.ResponseTypesSupported ??= ["code"];
264-
metadata.GrantTypesSupported ??= ["authorization_code", "refresh_token"];
265-
metadata.TokenEndpointAuthMethodsSupported ??= ["client_secret_post"];
266-
metadata.CodeChallengeMethodsSupported ??= ["S256"];
260+
continue;
261+
}
267262

268-
return metadata;
263+
if (metadata.AuthorizationEndpoint is null)
264+
{
265+
ThrowFailedToHandleUnauthorizedResponse($"No authorization_endpoint was provided via '{wellKnownEndpoint}'.");
269266
}
267+
268+
if (metadata.AuthorizationEndpoint.Scheme != Uri.UriSchemeHttp &&
269+
metadata.AuthorizationEndpoint.Scheme != Uri.UriSchemeHttps)
270+
{
271+
ThrowFailedToHandleUnauthorizedResponse($"AuthorizationEndpoint must use HTTP or HTTPS. '{metadata.AuthorizationEndpoint}' does not meet this requirement.");
272+
}
273+
274+
metadata.ResponseTypesSupported ??= ["code"];
275+
metadata.GrantTypesSupported ??= ["authorization_code", "refresh_token"];
276+
metadata.TokenEndpointAuthMethodsSupported ??= ["client_secret_post"];
277+
metadata.CodeChallengeMethodsSupported ??= ["S256"];
278+
279+
return metadata;
270280
}
271281
catch (Exception ex)
272282
{
273283
LogErrorFetchingAuthServerMetadata(ex, path);
274284
}
275285
}
276286

277-
return null;
287+
throw new McpException($"Failed to find .well-known/openid-configuration or .well-known/oauth-authorization-server metadata for authorization server: '{authServerUri}'");
278288
}
279289

280290
private async Task<TokenContainer> RefreshTokenAsync(string refreshToken, Uri resourceUri, AuthorizationServerMetadata authServerMetadata, CancellationToken cancellationToken)
@@ -320,11 +330,6 @@ private Uri BuildAuthorizationUrl(
320330
AuthorizationServerMetadata authServerMetadata,
321331
string codeChallenge)
322332
{
323-
if (authServerMetadata.AuthorizationEndpoint.Scheme != Uri.UriSchemeHttp &&
324-
authServerMetadata.AuthorizationEndpoint.Scheme != Uri.UriSchemeHttps)
325-
{
326-
throw new ArgumentException("AuthorizationEndpoint must use HTTP or HTTPS.", nameof(authServerMetadata));
327-
}
328333

329334
var queryParamsDictionary = new Dictionary<string, string>
330335
{

0 commit comments

Comments
 (0)