/* * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) * See https://github.com/openiddict/openiddict-core for more information concerning * the license and the contributors participating to this project. */ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Security.Claims; using System.Threading.Tasks; using AspNet.Security.OAuth.Validation; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Authentication; using Microsoft.AspNetCore.Http.Features.Authentication; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OpenIddict.Abstractions; using OpenIddict.Core; using OpenIddict.Models; using Xunit; namespace OpenIddict.Validation.Tests { public class OpenIddictValidationHandlerTests { [Fact] public async Task HandleAuthenticateAsync_InvalidTokenCausesInvalidAuthentication() { // Arrange var server = CreateResourceServer(); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "invalid-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } [Fact] public async Task HandleAuthenticateAsync_ValidTokenAllowsSuccessfulAuthentication() { // Arrange var server = CreateResourceServer(); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("Fabrikam", await response.Content.ReadAsStringAsync()); } [Fact] public async Task HandleAuthenticateAsync_MissingAudienceCausesInvalidAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.AddAudiences("http://www.fabrikam.com/"); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } [Fact] public async Task HandleAuthenticateAsync_InvalidAudienceCausesInvalidAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.AddAudiences("http://www.fabrikam.com/"); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token-with-single-audience"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } [Fact] public async Task HandleAuthenticateAsync_ValidAudienceAllowsSuccessfulAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.AddAudiences("http://www.fabrikam.com/"); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token-with-multiple-audiences"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("Fabrikam", await response.Content.ReadAsStringAsync()); } [Fact] public async Task HandleAuthenticateAsync_AnyMatchingAudienceCausesSuccessfulAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.AddAudiences("http://www.contoso.com/"); builder.AddAudiences("http://www.fabrikam.com/"); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token-with-single-audience"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("Fabrikam", await response.Content.ReadAsStringAsync()); } [Fact] public async Task HandleAuthenticateAsync_MultipleMatchingAudienceCausesSuccessfulAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.AddAudiences("http://www.contoso.com/"); builder.AddAudiences("http://www.fabrikam.com/"); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token-with-multiple-audiences"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("Fabrikam", await response.Content.ReadAsStringAsync()); } [Fact] public async Task HandleAuthenticateAsync_ExpiredTicketCausesInvalidAuthentication() { // Arrange var server = CreateResourceServer(); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "expired-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } [Fact] public async Task HandleAuthenticateAsync_AuthenticationTicketContainsRequiredClaims() { // Arrange var server = CreateResourceServer(); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/ticket"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token-with-scopes"); // Act var response = await client.SendAsync(request); var ticket = JObject.Parse(await response.Content.ReadAsStringAsync()); var claims = from claim in ticket.Value("Claims") select new { Type = claim.Value(nameof(Claim.Type)), Value = claim.Value(nameof(Claim.Value)) }; // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Contains(claims, claim => claim.Type == OAuthValidationConstants.Claims.Subject && claim.Value == "Fabrikam"); Assert.Contains(claims, claim => claim.Type == OAuthValidationConstants.Claims.Scope && claim.Value == "C54A8F5E-0387-43F4-BA43-FD4B50DC190D"); Assert.Contains(claims, claim => claim.Type == OAuthValidationConstants.Claims.Scope && claim.Value == "5C57E3BD-9EFB-4224-9AB8-C8C5E009FFD7"); } [Fact] public async Task HandleAuthenticateAsync_AuthenticationTicketContainsRequiredProperties() { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.SaveToken = true); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/ticket"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token"); // Act var response = await client.SendAsync(request); var ticket = JObject.Parse(await response.Content.ReadAsStringAsync()); var properties = from claim in ticket.Value("Properties") select new { Name = claim.Value("Name"), Value = claim.Value("Value") }; // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Contains(properties, property => property.Name == ".Token.access_token" && property.Value == "valid-token"); } [Fact] public async Task HandleAuthenticateAsync_InvalidReplacedTokenCausesInvalidAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.Events.OnRetrieveToken = context => { context.Token = "invalid-token"; return Task.FromResult(0); }); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } [Fact] public async Task HandleAuthenticateAsync_ValidReplacedTokenCausesSuccessfulAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.Events.OnRetrieveToken = context => { context.Token = "valid-token"; return Task.FromResult(0); }); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "invalid-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("Fabrikam", await response.Content.ReadAsStringAsync()); } [Fact] public async Task HandleAuthenticateAsync_SkipToNextMiddlewareFromReceiveTokenCausesInvalidAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.Events.OnRetrieveToken = context => { context.SkipToNextMiddleware(); return Task.FromResult(0); }); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } [Fact] public async Task HandleAuthenticateAsync_NullTicketAndHandleResponseFromReceiveTokenCauseInvalidAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.Events.OnRetrieveToken = context => { context.Ticket = null; context.HandleResponse(); return Task.FromResult(0); }); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } [Fact] public async Task HandleAuthenticateAsync_ReplacedTicketAndHandleResponseFromReceiveTokenCauseSuccessfulAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.Events.OnRetrieveToken = context => { var identity = new ClaimsIdentity(context.Options.AuthenticationScheme); identity.AddClaim(new Claim(OAuthValidationConstants.Claims.Subject, "Fabrikam")); context.Ticket = new AuthenticationTicket( new ClaimsPrincipal(identity), new AuthenticationProperties(), context.Options.AuthenticationScheme); context.HandleResponse(); return Task.FromResult(0); }); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "invalid-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("Fabrikam", await response.Content.ReadAsStringAsync()); } [Fact] public async Task HandleAuthenticateAsync_SkipToNextMiddlewareFromValidateTokenCausesInvalidAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.Events.OnValidateToken = context => { context.SkipToNextMiddleware(); return Task.FromResult(0); }); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } [Fact] public async Task HandleAuthenticateAsync_NullTicketAndHandleResponseFromValidateTokenCauseInvalidAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.Events.OnValidateToken = context => { context.Ticket = null; context.HandleResponse(); return Task.FromResult(0); }); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } [Fact] public async Task HandleAuthenticateAsync_ReplacedTicketAndHandleResponseFromValidateTokenCauseSuccessfulAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.Events.OnValidateToken = context => { var identity = new ClaimsIdentity(context.Options.AuthenticationScheme); identity.AddClaim(new Claim(OAuthValidationConstants.Claims.Subject, "Contoso")); context.Ticket = new AuthenticationTicket( new ClaimsPrincipal(identity), new AuthenticationProperties(), context.Options.AuthenticationScheme); context.HandleResponse(); return Task.FromResult(0); }); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("Contoso", await response.Content.ReadAsStringAsync()); } [Fact] public async Task HandleAuthenticateAsync_UpdatedTicketFromValidateTokenCausesSuccessfulAuthentication() { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.Events.OnValidateToken = context => { var identity = new ClaimsIdentity(context.Options.AuthenticationScheme); identity.AddClaim(new Claim(OAuthValidationConstants.Claims.Subject, "Contoso")); context.Ticket = new AuthenticationTicket( new ClaimsPrincipal(identity), new AuthenticationProperties(), context.Options.AuthenticationScheme); context.HandleResponse(); return Task.FromResult(0); }); }); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token"); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("Contoso", await response.Content.ReadAsStringAsync()); } [Fact] public async Task HandleUnauthorizedAsync_ErrorDetailsAreResolvedFromChallengeContext() { // Arrange var server = CreateResourceServer(builder => { builder.RemoveErrorDetails(); builder.SetRealm("global_realm"); builder.Configure(options => options.Events.OnApplyChallenge = context => { // Assert Assert.Equal(context.Error, "custom_error"); Assert.Equal(context.ErrorDescription, "custom_error_description"); Assert.Equal(context.ErrorUri, "custom_error_uri"); Assert.Equal(context.Realm, "custom_realm"); Assert.Equal(context.Scope, "custom_scope"); return Task.FromResult(0); }); }); var client = server.CreateClient(); // Act var response = await client.GetAsync("/challenge"); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); Assert.Equal(@"Bearer realm=""custom_realm"", error=""custom_error"", error_description=""custom_error_description"", " + @"error_uri=""custom_error_uri"", scope=""custom_scope""", response.Headers.WwwAuthenticate.ToString()); } [Theory] [InlineData("invalid-token", OAuthValidationConstants.Errors.InvalidToken, "The access token is not valid.")] [InlineData("expired-token", OAuthValidationConstants.Errors.InvalidToken, "The access token is no longer valid.")] public async Task HandleUnauthorizedAsync_ErrorDetailsAreInferredFromAuthenticationFailure( string token, string error, string description) { // Arrange var server = CreateResourceServer(); var client = server.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, "/"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); // Act var response = await client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); Assert.Equal($@"Bearer error=""{error}"", error_description=""{description}""", response.Headers.WwwAuthenticate.ToString()); } [Fact] public async Task HandleUnauthorizedAsync_ApplyChallenge_AllowsHandlingResponse() { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.Events.OnApplyChallenge = context => { context.HandleResponse(); context.HttpContext.Response.Headers["X-Custom-Authentication-Header"] = "Bearer"; return Task.FromResult(0); }); }); var client = server.CreateClient(); // Act var response = await client.GetAsync("/challenge"); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Empty(response.Headers.WwwAuthenticate); Assert.Equal(new[] { "Bearer" }, response.Headers.GetValues("X-Custom-Authentication-Header")); } [Fact] public async Task HandleUnauthorizedAsync_ApplyChallenge_AllowsSkippingToNextMiddleware() { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.Events.OnApplyChallenge = context => { context.SkipToNextMiddleware(); return Task.FromResult(0); }); }); var client = server.CreateClient(); // Act var response = await client.GetAsync("/challenge"); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Empty(response.Headers.WwwAuthenticate); Assert.Empty(await response.Content.ReadAsStringAsync()); } [Theory] [InlineData(null, null, null, null, null, "Bearer")] [InlineData("custom_error", null, null, null, null, @"Bearer error=""custom_error""")] [InlineData(null, "custom_error_description", null, null, null, @"Bearer error_description=""custom_error_description""")] [InlineData(null, null, "custom_error_uri", null, null, @"Bearer error_uri=""custom_error_uri""")] [InlineData(null, null, null, "custom_realm", null, @"Bearer realm=""custom_realm""")] [InlineData(null, null, null, null, "custom_scope", @"Bearer scope=""custom_scope""")] [InlineData("custom_error", "custom_error_description", "custom_error_uri", "custom_realm", "custom_scope", @"Bearer realm=""custom_realm"", error=""custom_error"", " + @"error_description=""custom_error_description"", " + @"error_uri=""custom_error_uri"", scope=""custom_scope""")] public async Task HandleUnauthorizedAsync_ReturnsExpectedWwwAuthenticateHeader( string error, string description, string uri, string realm, string scope, string header) { // Arrange var server = CreateResourceServer(builder => { builder.Configure(options => options.Events.OnApplyChallenge = context => { context.Error = error; context.ErrorDescription = description; context.ErrorUri = uri; context.Realm = realm; context.Scope = scope; return Task.FromResult(0); }); }); var client = server.CreateClient(); // Act var response = await client.GetAsync("/challenge"); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); Assert.Equal(header, response.Headers.WwwAuthenticate.ToString()); } private static TestServer CreateResourceServer(Action configuration = null) { var format = new Mock>(MockBehavior.Strict); format.Setup(mock => mock.Unprotect(It.Is(token => token == "invalid-token"))) .Returns(value: null); format.Setup(mock => mock.Unprotect(It.Is(token => token == "valid-token"))) .Returns(delegate { var identity = new ClaimsIdentity(OpenIddictValidationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(OAuthValidationConstants.Claims.Subject, "Fabrikam")); var properties = new AuthenticationProperties(); return new AuthenticationTicket(new ClaimsPrincipal(identity), properties, OpenIddictValidationDefaults.AuthenticationScheme); }); format.Setup(mock => mock.Unprotect(It.Is(token => token == "valid-token-with-scopes"))) .Returns(delegate { var identity = new ClaimsIdentity(OpenIddictValidationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(OAuthValidationConstants.Claims.Subject, "Fabrikam")); var properties = new AuthenticationProperties(); properties.Items[OAuthValidationConstants.Properties.Scopes] = @"[""C54A8F5E-0387-43F4-BA43-FD4B50DC190D"",""5C57E3BD-9EFB-4224-9AB8-C8C5E009FFD7""]"; return new AuthenticationTicket(new ClaimsPrincipal(identity), properties, OpenIddictValidationDefaults.AuthenticationScheme); }); format.Setup(mock => mock.Unprotect(It.Is(token => token == "valid-token-with-single-audience"))) .Returns(delegate { var identity = new ClaimsIdentity(OpenIddictValidationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(OAuthValidationConstants.Claims.Subject, "Fabrikam")); var properties = new AuthenticationProperties(new Dictionary { [OAuthValidationConstants.Properties.Audiences] = @"[""http://www.contoso.com/""]" }); return new AuthenticationTicket(new ClaimsPrincipal(identity), properties, OpenIddictValidationDefaults.AuthenticationScheme); }); format.Setup(mock => mock.Unprotect(It.Is(token => token == "valid-token-with-multiple-audiences"))) .Returns(delegate { var identity = new ClaimsIdentity(OpenIddictValidationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(OAuthValidationConstants.Claims.Subject, "Fabrikam")); var properties = new AuthenticationProperties(new Dictionary { [OAuthValidationConstants.Properties.Audiences] = @"[""http://www.contoso.com/"",""http://www.fabrikam.com/""]" }); return new AuthenticationTicket(new ClaimsPrincipal(identity), properties, OpenIddictValidationDefaults.AuthenticationScheme); }); format.Setup(mock => mock.Unprotect(It.Is(token => token == "expired-token"))) .Returns(delegate { var identity = new ClaimsIdentity(OpenIddictValidationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(OAuthValidationConstants.Claims.Subject, "Fabrikam")); var properties = new AuthenticationProperties(); properties.ExpiresUtc = DateTimeOffset.UtcNow - TimeSpan.FromDays(1); return new AuthenticationTicket(new ClaimsPrincipal(identity), properties, OpenIddictValidationDefaults.AuthenticationScheme); }); var builder = new WebHostBuilder(); builder.UseEnvironment("Testing"); builder.ConfigureLogging(options => options.AddDebug()); builder.ConfigureServices(services => { services.AddOpenIddict() .AddCore(options => { options.UseDefaultModels(); // Replace the default OpenIddict managers. options.Services.AddSingleton(CreateApplicationManager()); options.Services.AddSingleton(CreateAuthorizationManager()); options.Services.AddSingleton(CreateScopeManager()); options.Services.AddSingleton(CreateTokenManager()); }) .AddValidation(options => { options.Configure(settings => settings.AccessTokenFormat = format.Object); // Note: overriding the default data protection provider is not necessary for the tests to pass, // but is useful to ensure unnecessary keys are not persisted in testing environments, which also // helps make the unit tests run faster, as no registry or disk access is required in this case. options.UseDataProtectionProvider(new EphemeralDataProtectionProvider()); // Run the configuration delegate // registered by the unit tests. configuration?.Invoke(options); }); }); builder.Configure(app => { app.UseOpenIddictValidation(); app.Map("/ticket", map => map.Run(async context => { var ticket = new AuthenticateContext(OpenIddictValidationDefaults.AuthenticationScheme); await context.Authentication.AuthenticateAsync(ticket); if (!ticket.Accepted || ticket.Principal == null || ticket.Properties == null) { await context.Authentication.ChallengeAsync(); return; } context.Response.ContentType = "application/json"; // Return the authentication ticket as a JSON object. await context.Response.WriteAsync(JsonConvert.SerializeObject(new { Claims = from claim in ticket.Principal.Claims select new { claim.Type, claim.Value }, Properties = from property in ticket.Properties select new { Name = property.Key, property.Value } })); })); app.Map("/challenge", map => map.Run(context => { var properties = new AuthenticationProperties(new Dictionary { [OAuthValidationConstants.Properties.Error] = "custom_error", [OAuthValidationConstants.Properties.ErrorDescription] = "custom_error_description", [OAuthValidationConstants.Properties.ErrorUri] = "custom_error_uri", [OAuthValidationConstants.Properties.Realm] = "custom_realm", [OAuthValidationConstants.Properties.Scope] = "custom_scope", }); return context.Authentication.ChallengeAsync(OpenIddictValidationDefaults.AuthenticationScheme, properties); })); app.Run(context => { if (!context.User.Identities.Any(identity => identity.IsAuthenticated)) { return context.Authentication.ChallengeAsync(); } var identifier = context.User.FindFirst(OAuthValidationConstants.Claims.Subject).Value; return context.Response.WriteAsync(identifier); }); }); return new TestServer(builder); } private static OpenIddictApplicationManager CreateApplicationManager( Action>> configuration = null) { var manager = new Mock>( Mock.Of(), Mock.Of>>(), Mock.Of>()); configuration?.Invoke(manager); return manager.Object; } private static OpenIddictAuthorizationManager CreateAuthorizationManager( Action>> configuration = null) { var manager = new Mock>( Mock.Of(), Mock.Of>>(), Mock.Of>()); configuration?.Invoke(manager); return manager.Object; } private static OpenIddictScopeManager CreateScopeManager( Action>> configuration = null) { var manager = new Mock>( Mock.Of(), Mock.Of>>(), Mock.Of>()); configuration?.Invoke(manager); return manager.Object; } private static OpenIddictTokenManager CreateTokenManager( Action>> configuration = null) { var manager = new Mock>( Mock.Of(), Mock.Of>>(), Mock.Of>()); configuration?.Invoke(manager); return manager.Object; } } }