diff --git a/Tests/MCPify.Tests/ClientCredentialsAuthenticationTests.cs b/Tests/MCPify.Tests/ClientCredentialsAuthenticationTests.cs index c545db3..60a90f3 100644 --- a/Tests/MCPify.Tests/ClientCredentialsAuthenticationTests.cs +++ b/Tests/MCPify.Tests/ClientCredentialsAuthenticationTests.cs @@ -105,28 +105,22 @@ public TestClientCredentialsServer() { var port = GetRandomUnusedPort(); BaseUrl = $"http://localhost:{port}"; - _host = Host.CreateDefaultBuilder() - .ConfigureWebHostDefaults(builder => + + var builder = WebApplication.CreateBuilder(); + builder.WebHost.UseUrls(BaseUrl); + + var app = builder.Build(); + app.MapPost("/token", async context => + { + await context.Response.WriteAsJsonAsync(new { - builder.UseUrls(BaseUrl); - builder.Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapPost("/token", async context => - { - await context.Response.WriteAsJsonAsync(new - { - access_token = "cc_token", - token_type = "Bearer", - expires_in = 3600 - }); - }); - }); - }); - }) - .Build(); + access_token = "cc_token", + token_type = "Bearer", + expires_in = 3600 + }); + }); + + _host = app; } public async Task StartAsync() => await _host.StartAsync(); diff --git a/Tests/MCPify.Tests/Integration/OAuthChallengeTokenValidationTests.cs b/Tests/MCPify.Tests/Integration/OAuthChallengeTokenValidationTests.cs index 4b5a033..ba110f7 100644 --- a/Tests/MCPify.Tests/Integration/OAuthChallengeTokenValidationTests.cs +++ b/Tests/MCPify.Tests/Integration/OAuthChallengeTokenValidationTests.cs @@ -17,37 +17,7 @@ public class OAuthChallengeTokenValidationTests [Fact] public async Task PostWithoutSession_ReturnsUnauthorizedChallenge_WhenTokenValidationEnabled() { - using var host = await new HostBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder - .UseTestServer() - .ConfigureServices(services => - { - services.AddLogging(); - services.AddRouting(); - services.AddMcpify(options => - { - options.Transport = McpTransportType.Http; - options.TokenValidation = new TokenValidationOptions - { - EnableJwtValidation = true, - ValidateAudience = true - }; - }); - }) - .Configure(app => - { - app.UseRouting(); - app.UseMcpifyContext(); - app.UseMcpifyOAuth(); - app.UseEndpoints(endpoints => - { - endpoints.MapMcpifyEndpoint(); - }); - }); - }) - .StartAsync(); + await using var host = await CreateHostAsync(); var options = host.Services.GetRequiredService(); Assert.True(options.TokenValidation?.EnableJwtValidation, "Token validation should be enabled"); @@ -73,4 +43,29 @@ public async Task PostWithoutSession_ReturnsUnauthorizedChallenge_WhenTokenValid Assert.Contains(response.Headers.WwwAuthenticate, header => string.Equals(header.Scheme, "Bearer", StringComparison.OrdinalIgnoreCase)); } + + private static async Task CreateHostAsync() + { + var builder = WebApplication.CreateBuilder(); + builder.WebHost.UseTestServer(); + + builder.Services.AddLogging(); + builder.Services.AddMcpify(options => + { + options.Transport = McpTransportType.Http; + options.TokenValidation = new TokenValidationOptions + { + EnableJwtValidation = true, + ValidateAudience = true + }; + }); + + var app = builder.Build(); + app.UseMcpifyContext(); + app.UseMcpifyOAuth(); + app.MapMcpifyEndpoint(); + + await app.StartAsync(); + return app; + } } diff --git a/Tests/MCPify.Tests/Integration/OAuthMetadataEndpointTests.cs b/Tests/MCPify.Tests/Integration/OAuthMetadataEndpointTests.cs index c483dfa..3449ab9 100644 --- a/Tests/MCPify.Tests/Integration/OAuthMetadataEndpointTests.cs +++ b/Tests/MCPify.Tests/Integration/OAuthMetadataEndpointTests.cs @@ -113,33 +113,24 @@ public async Task GetMetadata_FallsBackToAuthorizationUrlAuthority_WhenAuthoriza Assert.Contains("https://auth.example.com", metadata!.AuthorizationServers); } - private async Task CreateHostAsync(Action? configure = null, Action? configureOptions = null) + private static async Task CreateHostAsync(Action? configure = null, Action? configureOptions = null) { - return await new HostBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder - .UseTestServer() - .ConfigureServices(services => - { - services.AddMcpify(options => - { - configureOptions?.Invoke(options); - }); - services.AddLogging(); - services.AddRouting(); - }) - .Configure(app => - { - configure?.Invoke(app.ApplicationServices); - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapMcpifyEndpoint(); - }); - }); - }) - .StartAsync(); + var builder = WebApplication.CreateBuilder(); + builder.WebHost.UseTestServer(); + + builder.Services.AddMcpify(options => + { + configureOptions?.Invoke(options); + }); + builder.Services.AddLogging(); + + var app = builder.Build(); + + configure?.Invoke(app.Services); + app.MapMcpifyEndpoint(); + + await app.StartAsync(); + return app; } private class ProtectedResourceMetadata diff --git a/Tests/MCPify.Tests/Integration/TestApiServer.cs b/Tests/MCPify.Tests/Integration/TestApiServer.cs index d92fbe5..a63178c 100644 --- a/Tests/MCPify.Tests/Integration/TestApiServer.cs +++ b/Tests/MCPify.Tests/Integration/TestApiServer.cs @@ -18,47 +18,42 @@ public TestApiServer() var port = GetRandomUnusedPort(); BaseUrl = $"http://localhost:{port}"; - _host = Host.CreateDefaultBuilder() - .ConfigureWebHostDefaults(builder => - { - builder.UseUrls(BaseUrl); - builder.Configure(ConfigureApp); - }) - .Build(); + var builder = WebApplication.CreateBuilder(); + builder.WebHost.UseUrls(BaseUrl); + + var app = builder.Build(); + ConfigureApp(app); + _host = app; } public async Task StartAsync() => await _host.StartAsync(); public HttpClient CreateClient() => new() { BaseAddress = new Uri(BaseUrl) }; - private void ConfigureApp(IApplicationBuilder app) + private void ConfigureApp(WebApplication app) { - app.UseRouting(); - app.UseEndpoints(endpoints => + app.MapGet("/users/{id:int}", async context => { - endpoints.MapGet("/users/{id:int}", async context => + var id = int.Parse(context.Request.RouteValues["id"]?.ToString() ?? "0"); + await context.Response.WriteAsJsonAsync(new { - var id = int.Parse(context.Request.RouteValues["id"]?.ToString() ?? "0"); - await context.Response.WriteAsJsonAsync(new - { - id, - path = context.Request.Path.Value, - query = context.Request.QueryString.Value - }); + id, + path = context.Request.Path.Value, + query = context.Request.QueryString.Value }); + }); - endpoints.MapPost("/echo", async context => - { - using var reader = new StreamReader(context.Request.Body); - var body = await reader.ReadToEndAsync(); - await context.Response.WriteAsync(body); - }); + app.MapPost("/echo", async context => + { + using var reader = new StreamReader(context.Request.Body); + var body = await reader.ReadToEndAsync(); + await context.Response.WriteAsync(body); + }); - endpoints.MapGet("/auth-check", async context => - { - var auth = context.Request.Headers.Authorization.ToString(); - await context.Response.WriteAsJsonAsync(new { authorization = auth }); - }); + app.MapGet("/auth-check", async context => + { + var auth = context.Request.Headers.Authorization.ToString(); + await context.Response.WriteAsJsonAsync(new { authorization = auth }); }); } diff --git a/Tests/MCPify.Tests/Integration/TestOAuthServer.cs b/Tests/MCPify.Tests/Integration/TestOAuthServer.cs index 6ed25e7..8faf626 100644 --- a/Tests/MCPify.Tests/Integration/TestOAuthServer.cs +++ b/Tests/MCPify.Tests/Integration/TestOAuthServer.cs @@ -47,13 +47,12 @@ public TestOAuthServer() _jwk.KeyId = _signingKey.KeyId; _jwk.Alg = SecurityAlgorithms.RsaSha256; - _host = Host.CreateDefaultBuilder() - .ConfigureWebHostDefaults(builder => - { - builder.UseUrls(BaseUrl); - builder.Configure(ConfigureApp); - }) - .Build(); + var builder = WebApplication.CreateBuilder(); + builder.WebHost.UseUrls(BaseUrl); + + var app = builder.Build(); + ConfigureApp(app); + _host = app; } public async Task StartAsync() => await _host.StartAsync(); @@ -68,137 +67,133 @@ public void AuthorizeDevice() public HttpClient CreateClient() => new() { BaseAddress = new Uri(BaseUrl) }; - private void ConfigureApp(IApplicationBuilder app) + private void ConfigureApp(WebApplication app) { - app.UseRouting(); - app.UseEndpoints(endpoints => + app.MapGet("/.well-known/openid-configuration", async context => { - endpoints.MapGet("/.well-known/openid-configuration", async context => + await context.Response.WriteAsJsonAsync(new { - await context.Response.WriteAsJsonAsync(new - { - issuer = BaseUrl, - authorization_endpoint = AuthorizationEndpoint, - token_endpoint = TokenEndpoint, - device_authorization_endpoint = DeviceCodeEndpoint, - jwks_uri = JwksEndpoint, - response_types_supported = new[] { "code" }, - grant_types_supported = new[] { "authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code" }, - id_token_signing_alg_values_supported = new[] { SecurityAlgorithms.RsaSha256 }, - scopes_supported = new[] { "openid", "profile", "read_secrets" } - }); + issuer = BaseUrl, + authorization_endpoint = AuthorizationEndpoint, + token_endpoint = TokenEndpoint, + device_authorization_endpoint = DeviceCodeEndpoint, + jwks_uri = JwksEndpoint, + response_types_supported = new[] { "code" }, + grant_types_supported = new[] { "authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code" }, + id_token_signing_alg_values_supported = new[] { SecurityAlgorithms.RsaSha256 }, + scopes_supported = new[] { "openid", "profile", "read_secrets" } }); + }); - endpoints.MapGet("/jwks", async context => - { - await context.Response.WriteAsJsonAsync(new { keys = new[] { _jwk } }); - }); + app.MapGet("/jwks", async context => + { + await context.Response.WriteAsJsonAsync(new { keys = new[] { _jwk } }); + }); + + app.MapGet("/authorize", async context => + { + var redirectUri = context.Request.Query["redirect_uri"]; + var state = context.Request.Query["state"]; + var code = "auth_code_" + Guid.NewGuid(); - endpoints.MapGet("/authorize", async context => + lock (_lock) { - var redirectUri = context.Request.Query["redirect_uri"]; - var state = context.Request.Query["state"]; - var code = "auth_code_" + Guid.NewGuid(); + _lastAuthCode = code; + _lastRefreshToken = "refresh_" + Guid.NewGuid(); + } - lock (_lock) - { - _lastAuthCode = code; - _lastRefreshToken = "refresh_" + Guid.NewGuid(); - } + context.Response.Redirect($"{redirectUri}?code={code}&state={state}"); + await Task.CompletedTask; + }); - context.Response.Redirect($"{redirectUri}?code={code}&state={state}"); - await Task.CompletedTask; - }); + app.MapPost("/token", async context => + { + var form = await context.Request.ReadFormAsync(); + var grantType = form["grant_type"].ToString(); - endpoints.MapPost("/token", async context => + if (grantType == "client_credentials") { - var form = await context.Request.ReadFormAsync(); - var grantType = form["grant_type"].ToString(); - - if (grantType == "client_credentials") + await context.Response.WriteAsJsonAsync(new { - await context.Response.WriteAsJsonAsync(new - { - access_token = ClientCredentialsToken, - token_type = "Bearer", - expires_in = 3600 - }); - return; - } + access_token = ClientCredentialsToken, + token_type = "Bearer", + expires_in = 3600 + }); + return; + } - if (grantType == "authorization_code") + if (grantType == "authorization_code") + { + if (!string.Equals(form["code"], _lastAuthCode, StringComparison.Ordinal)) { - if (!string.Equals(form["code"], _lastAuthCode, StringComparison.Ordinal)) - { - context.Response.StatusCode = (int)HttpStatusCode.BadRequest; - await context.Response.WriteAsync("Invalid code"); - return; - } - - await WriteTokenResponse(context); + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + await context.Response.WriteAsync("Invalid code"); return; } - if (grantType == "refresh_token") + await WriteTokenResponse(context); + return; + } + + if (grantType == "refresh_token") + { + await WriteTokenResponse(context); + return; + } + + if (grantType == "urn:ietf:params:oauth:grant-type:device_code") + { + if (!string.Equals(form["device_code"], _lastDeviceCode, StringComparison.Ordinal)) { - await WriteTokenResponse(context); + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + await context.Response.WriteAsync("{\"error\": \"invalid_device_code\"}"); return; } - if (grantType == "urn:ietf:params:oauth:grant-type:device_code") + if (!_deviceAuthorized) { - if (!string.Equals(form["device_code"], _lastDeviceCode, StringComparison.Ordinal)) - { - context.Response.StatusCode = (int)HttpStatusCode.BadRequest; - await context.Response.WriteAsync("{\"error\": \"invalid_device_code\"}"); - return; - } - - if (!_deviceAuthorized) - { - context.Response.StatusCode = (int)HttpStatusCode.BadRequest; - await context.Response.WriteAsync("{\"error\": \"authorization_pending\"}"); - return; - } - - await WriteTokenResponse(context); + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + await context.Response.WriteAsync("{\"error\": \"authorization_pending\"}"); return; } - context.Response.StatusCode = (int)HttpStatusCode.BadRequest; - await context.Response.WriteAsync("{\"error\": \"unsupported_grant_type\"}"); - }); + await WriteTokenResponse(context); + return; + } - endpoints.MapPost("/device/code", async context => - { - var deviceCode = "dc_" + Guid.NewGuid(); - var userCode = "UC-" + Random.Shared.Next(1000, 9999); + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + await context.Response.WriteAsync("{\"error\": \"unsupported_grant_type\"}"); + }); - lock (_lock) - { - _lastDeviceCode = deviceCode; - _deviceAuthorized = false; - } + app.MapPost("/device/code", async context => + { + var deviceCode = "dc_" + Guid.NewGuid(); + var userCode = "UC-" + Random.Shared.Next(1000, 9999); - await context.Response.WriteAsJsonAsync(new - { - device_code = deviceCode, - user_code = userCode, - verification_uri = VerificationEndpoint, - expires_in = 300, - interval = 1 - }); + lock (_lock) + { + _lastDeviceCode = deviceCode; + _deviceAuthorized = false; + } + + await context.Response.WriteAsJsonAsync(new + { + device_code = deviceCode, + user_code = userCode, + verification_uri = VerificationEndpoint, + expires_in = 300, + interval = 1 }); + }); - endpoints.MapGet("/device/verify", async context => + app.MapGet("/device/verify", async context => + { + lock (_lock) { - lock (_lock) - { - _deviceAuthorized = true; - } + _deviceAuthorized = true; + } - await context.Response.WriteAsync("Device authorized."); - }); + await context.Response.WriteAsync("Device authorized."); }); }