|
2 | 2 | using Microsoft.Extensions.Options; |
3 | 3 | using ModelContextProtocol.AspNetCore; |
4 | 4 | using ModelContextProtocol.Protocol.Types; |
| 5 | +using ModelContextProtocol.AspNetCore.Auth; |
5 | 6 | using System.Security.Claims; |
6 | 7 | using System.Text.Encodings.Web; |
7 | 8 |
|
|
75 | 76 | }); |
76 | 77 |
|
77 | 78 | // Configure authentication using the built-in authentication system |
78 | | -builder.Services.AddAuthentication("Bearer") |
79 | | - .AddScheme<AuthenticationSchemeOptions, SimpleAuthHandler>("Bearer", options => { }); |
| 79 | +// Register "Bearer" scheme with our SimpleAuthHandler and set it as the default scheme |
| 80 | +builder.Services.AddAuthentication(options => |
| 81 | +{ |
| 82 | + options.DefaultScheme = "Bearer"; |
| 83 | + options.DefaultChallengeScheme = "Bearer"; // Ensure challenges use Bearer scheme |
| 84 | +}) |
| 85 | +.AddScheme<AuthenticationSchemeOptions, SimpleAuthHandler>("Bearer", options => { }); |
80 | 86 |
|
81 | 87 | // Add authorization policy for MCP |
82 | 88 | builder.Services.AddAuthorization(options => |
|
117 | 123 | // In a real app, you'd use a JWT handler or other proper authentication |
118 | 124 | class SimpleAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions> |
119 | 125 | { |
| 126 | + // Directly inject the ResourceMetadataService instead of the options |
| 127 | + private readonly ResourceMetadataService _resourceMetadataService; |
| 128 | + |
120 | 129 | public SimpleAuthHandler( |
121 | 130 | IOptionsMonitor<AuthenticationSchemeOptions> options, |
122 | 131 | ILoggerFactory logger, |
123 | | - UrlEncoder encoder) |
| 132 | + UrlEncoder encoder, |
| 133 | + ResourceMetadataService resourceMetadataService) |
124 | 134 | : base(options, logger, encoder) |
125 | 135 | { |
| 136 | + _resourceMetadataService = resourceMetadataService; |
126 | 137 | } |
127 | 138 |
|
128 | 139 | protected override Task<AuthenticateResult> HandleAuthenticateAsync() |
@@ -162,4 +173,19 @@ protected override Task<AuthenticateResult> HandleAuthenticateAsync() |
162 | 173 |
|
163 | 174 | return Task.FromResult(AuthenticateResult.Success(ticket)); |
164 | 175 | } |
| 176 | + |
| 177 | + protected override Task HandleChallengeAsync(AuthenticationProperties properties) |
| 178 | + { |
| 179 | + // Always include the resource_metadata in the WWW-Authenticate header |
| 180 | + var baseUrl = $"{Request.Scheme}://{Request.Host}"; |
| 181 | + var metadataUrl = $"{baseUrl}/.well-known/oauth-protected-resource"; |
| 182 | + |
| 183 | + // Add WWW-Authenticate header with resource_metadata |
| 184 | + Response.Headers.WWWAuthenticate = $"Bearer resource_metadata=\"{metadataUrl}\""; |
| 185 | + |
| 186 | + // Set 401 status code |
| 187 | + Response.StatusCode = 401; |
| 188 | + |
| 189 | + return Task.CompletedTask; |
| 190 | + } |
165 | 191 | } |
0 commit comments