/* * 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.Collections.Immutable; using System.Net.Http; using System.Security.Claims; using Microsoft.Extensions.DependencyInjection; using Moq; using Xunit; using static OpenIddict.Server.OpenIddictServerEvents; using static OpenIddict.Server.OpenIddictServerHandlers.Protection; namespace OpenIddict.Server.IntegrationTests; public abstract partial class OpenIddictServerIntegrationTests { [Theory] [InlineData(nameof(HttpMethod.Delete))] [InlineData(nameof(HttpMethod.Get))] [InlineData(nameof(HttpMethod.Head))] [InlineData(nameof(HttpMethod.Options))] [InlineData(nameof(HttpMethod.Put))] [InlineData(nameof(HttpMethod.Trace))] public async Task ExtractTokenRequest_UnexpectedMethodReturnsAnError(string method) { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.SendAsync(method, "/connect/token", new OpenIddictRequest()); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2084), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2084), response.ErrorUri); } [Theory] [InlineData("custom_error", null, null)] [InlineData("custom_error", "custom_description", null)] [InlineData("custom_error", "custom_description", "custom_uri")] [InlineData(null, "custom_description", null)] [InlineData(null, "custom_description", "custom_uri")] [InlineData(null, null, "custom_uri")] [InlineData(null, null, null)] public async Task ExtractTokenRequest_AllowsRejectingRequest(string? error, string? description, string? uri) { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Reject(error, description, uri); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest()); // Assert Assert.Equal(error ?? Errors.InvalidRequest, response.Error); Assert.Equal(description, response.ErrorDescription); Assert.Equal(uri, response.ErrorUri); } [Fact] public async Task ExtractTokenRequest_AllowsHandlingResponse() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Transaction.SetProperty("custom_response", new { name = "Bob le Bricoleur" }); context.HandleRequest(); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest()); // Assert Assert.Equal("Bob le Bricoleur", (string?) response["name"]); } [Fact] public async Task ExtractTokenRequest_AllowsSkippingHandler() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest()); // Assert Assert.Equal("Bob le Magnifique", (string?) response["name"]); } [Fact] public async Task ValidateTokenRequest_MissingGrantTypeCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = null }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2029(Parameters.GrantType), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_MissingClientIdCausesAnErrorForCodeFlowRequests() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = null, Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2029(Parameters.ClientId), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_MissingClientIdCausesAnErrorForClientCredentialsRequests() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientAssertion = null, ClientAssertionType = ClientAssertionTypes.JwtBearer, ClientId = null, GrantType = GrantTypes.ClientCredentials }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2029(Parameters.ClientId), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_MissingCodeCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = null, GrantType = GrantTypes.AuthorizationCode }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2029(Parameters.Code), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_MissingRefreshTokenCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = null }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2029(Parameters.RefreshToken), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_MissingSubjectTokenCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, SubjectToken = null }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2029(Parameters.SubjectToken), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_MissingSubjectTokenTypeCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = null }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2029(Parameters.SubjectTokenType), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_MissingActorTokenCausesAnErrorWhenActorTokenTypeIsSpecified() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = null, ActorTokenType = TokenTypeIdentifiers.AccessToken, GrantType = GrantTypes.TokenExchange, SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = TokenTypeIdentifiers.AccessToken }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2037(Parameters.ActorTokenType, Parameters.ActorToken), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2037), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_MissingActorTokenTypeCausesAnErrorWhenActorTokenIsSpecified() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = null, GrantType = GrantTypes.TokenExchange, SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = TokenTypeIdentifiers.AccessToken }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2037(Parameters.ActorToken, Parameters.ActorTokenType), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2037), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_UnsupportedSubjectTokenTypeCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = "custom_token_type" }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2032(Parameters.SubjectTokenType), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2032), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_UnsupportedActorTokenTypeCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = "custom_token_type", GrantType = GrantTypes.TokenExchange, SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = TokenTypeIdentifiers.AccessToken }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2032(Parameters.ActorTokenType), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2032), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_UnsupportedRequestedTokenTypeCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, GrantType = GrantTypes.TokenExchange, RequestedTokenType = "custom_token_type", SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = TokenTypeIdentifiers.AccessToken }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2032(Parameters.RequestedTokenType), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2032), response.ErrorUri); } [Theory] [InlineData(null, null)] [InlineData("username", null)] [InlineData(null, "password")] public async Task ValidateTokenRequest_MissingUserCredentialsCauseAnError(string? username, string? password) { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = username, Password = password }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2059(Parameters.Username, Parameters.Password), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2059), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_AuthorizationCodeRequestIsRejectedWhenPkceIsRequiredAndCodeVerifierIsMissing() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.RequireProofKeyForCodeExchange(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", CodeVerifier = null, GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2029(Parameters.CodeVerifier), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_AuthorizationCodeRequestIsValidatedWhenPkceIsNotRequiredAndCodeVerifierIsMissing() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", CodeVerifier = null, GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.NotNull(response.AccessToken); } [Fact] public async Task ValidateTokenRequest_AuthorizationCodeCausesAnErrorWhenScopeIsSent() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.RegisterScopes(Scopes.Phone, Scopes.Profile); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode, Scope = "profile phone" }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2074(Parameters.Scope), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2074), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_DeviceAuthorizationCodeCausesAnErrorWhenScopeIsSent() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.RegisterScopes(Scopes.Phone, Scopes.Profile); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", DeviceCode = "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS", GrantType = GrantTypes.DeviceCode, Scope = "profile phone" }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2074(Parameters.Scope), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2074), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_ForbiddenAudienceCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { Audiences = ["Contoso"], ClientId = "Fabrikam", DeviceCode = "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS", GrantType = GrantTypes.DeviceCode }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2195(Parameters.Audience), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2195), response.ErrorUri); } [Theory] [InlineData("fabrikam", SR.ID2030)] [InlineData("/path", SR.ID2030)] [InlineData("/tmp/file.xml", SR.ID2030)] [InlineData("C:\\tmp\\file.xml", SR.ID2030)] [InlineData("http://www.fabrikam.com/path#param=value", SR.ID2031)] [InlineData("urn:fabrikam#param", SR.ID2031)] public async Task ValidateTokenRequest_InvalidResourceCausesAnError(string resource, string message) { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, Resources = [resource], SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = TokenTypeIdentifiers.AccessToken }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(string.Format(SR.GetResourceString(message), Parameters.Resource), response.ErrorDescription); Assert.Equal(SR.FormatID8000(message), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_InvalidAuthorizationCodeCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2001), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2001), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_InvalidRefreshTokenCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2003), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2003), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_InvalidSubjectTokenCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = TokenTypeIdentifiers.AccessToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2004), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2004), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_InvalidActorTokenCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { if (!string.Equals(context.Token, "8xLOxBtZp8", StringComparison.Ordinal)) { return ValueTask.CompletedTask; } Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2004), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2004), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_ExpiredAuthorizationCodeCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetExpirationDate(TimeProvider.System.GetUtcNow() - TimeSpan.FromDays(1)) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2016), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2016), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_ExpiredRefreshTokenCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetExpirationDate(TimeProvider.System.GetUtcNow() - TimeSpan.FromDays(1)) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2018), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2018), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_ExpiredSubjectTokenCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetExpirationDate(TimeProvider.System.GetUtcNow() - TimeSpan.FromDays(1)) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2018), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2018), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_ExpiredActorTokenCausesAnError() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { if (string.Equals(context.Token, "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", StringComparison.Ordinal)) { Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetExpirationDate(TimeProvider.System.GetUtcNow() - TimeSpan.FromDays(1)) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; } Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2019), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2019), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_ExpiredDeviceCodeCausesAnError() { // Arrange var token = new OpenIddictToken(); var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByReferenceIdAsync("g43LaWCUrz2RaLILz2L1bg1bOpMSv1hGrH12IIkB9H4", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.Private.DeviceCode); mock.Setup(manager => manager.HasTypeAsync(token, TokenTypeIdentifiers.Private.DeviceCode, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetPayloadAsync(token, It.IsAny())) .ReturnsAsync("GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS"); mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetExpirationDateAsync(token, It.IsAny())) .ReturnsAsync(TimeProvider.System.GetUtcNow() - TimeSpan.FromDays(1)); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.Private.DeviceCode); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.DeviceCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Bricoleur") .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetTokenType(TokenTypeIdentifiers.Private.DeviceCode); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.DeviceCode, DeviceCode = "g43LaWCUrz2RaLILz2L1bg1bOpMSv1hGrH12IIkB9H4" }); // Assert Assert.Equal(Errors.ExpiredToken, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2017), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2017), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(token, Statuses.Inactive, It.IsAny()), Times.Never()); } [Fact] public async Task ValidateTokenRequest_AuthorizationCodeCausesAnErrorWhenPresentersAreMissing() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters(Enumerable.Empty()) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode }); }); Assert.Equal(SR.GetResourceString(SR.ID0043), exception.Message); } [Fact] public async Task ValidateTokenRequest_AuthorizationCodeCausesAnErrorWhenCallerIsNotAPresenter() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Contoso") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2069), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2069), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RefreshTokenCausesAnErrorWhenCallerIsNotAPresenter() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetPresenters("Contoso") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2071), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2071), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_SubjectTokenCausesAnErrorWhenCallerIsNotIdentifiedAndAudiencesAndPresentersAreAttached() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", context.Token); Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetAudiences("Litware") .SetPresenters("Contoso") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = TokenTypeIdentifiers.AccessToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2186), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2186), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_TokenRequestSucceedsWhenSubjectTokenIsNotSenderConstrained() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", context.Token); Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetAudiences("Litware") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", GrantType = GrantTypes.TokenExchange, SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = TokenTypeIdentifiers.AccessToken }); // Assert Assert.NotNull(response.AccessToken); } [Fact] public async Task ValidateTokenRequest_TokenRequestSucceedsWhenSubjectTokenIsNotReceiverConstrained() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", context.Token); Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetPresenters("Contoso") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", GrantType = GrantTypes.TokenExchange, SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = TokenTypeIdentifiers.AccessToken }); // Assert Assert.NotNull(response.AccessToken); } [Fact] public async Task ValidateTokenRequest_SubjectTokenCausesAnErrorWhenCallerIsNotAnAudienceOrPresenter() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", context.Token); Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetAudiences("Litware") .SetPresenters("Contoso") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", GrantType = GrantTypes.TokenExchange, SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = TokenTypeIdentifiers.AccessToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2187), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2187), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_ActorTokenCausesAnErrorWhenCallerIsNotIdentifiedAndAudiencesAndPresentersAreAttached() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { if (string.Equals(context.Token, "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", StringComparison.Ordinal)) { Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetAudiences("Litware") .SetPresenters("Contoso") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; } Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2188), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2188), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_TokenRequestSucceedsWhenActorTokenIsNotSenderConstrained() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { if (string.Equals(context.Token, "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", StringComparison.Ordinal)) { Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetAudiences("Litware") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; } Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, ClientId = "Fabrikam", GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.NotNull(response.AccessToken); } [Fact] public async Task ValidateTokenRequest_TokenRequestSucceedsWhenActorTokenIsNotReceiverConstrained() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { if (string.Equals(context.Token, "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", StringComparison.Ordinal)) { Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetPresenters("Contoso") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; } Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, ClientId = "Fabrikam", GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.NotNull(response.AccessToken); } [Fact] public async Task ValidateTokenRequest_ActorTokenCausesAnErrorWhenCallerIsNotAnAudienceOrPresenter() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { if (string.Equals(context.Token, "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", StringComparison.Ordinal)) { Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetAudiences("Litware") .SetPresenters("Contoso") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; } Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, ClientId = "Fabrikam", GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2189), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2189), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_AuthorizationCodeCausesAnErrorWhenRedirectUriIsMissing() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetClaim(Claims.Subject, "Bob le Bricoleur") .SetClaim(Claims.Private.RedirectUri, "http://www.fabrikam.com/callback"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode, RedirectUri = null }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2029(Parameters.RedirectUri), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_AuthorizationCodeCausesAnErrorWhenRedirectUriIsInvalid() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetClaim(Claims.Subject, "Bob le Bricoleur") .SetClaim(Claims.Private.RedirectUri, "http://www.fabrikam.com/callback"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.contoso.com/redirect_uri" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.FormatID2072(Parameters.RedirectUri), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2072), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RequestCausesErrorWhenSendingCodeVerifier() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", CodeVerifier = "AbCd97394879834759873497549237098273498072304987523948673248972349857982345", GrantType = GrantTypes.AuthorizationCode }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2073(Parameters.CodeVerifier, Parameters.CodeChallenge), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2073), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_AuthorizationCodeCausesAnErrorWhenCodeVerifierIsMissing() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetClaim(Claims.Subject, "Bob le Bricoleur") .SetClaim(Claims.Private.CodeChallenge, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM") .SetClaim(Claims.Private.CodeChallengeMethod, CodeChallengeMethods.Sha256); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", CodeVerifier = null, GrantType = GrantTypes.AuthorizationCode }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2029(Parameters.CodeVerifier), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_AuthorizationCodeCausesAnErrorWhenCodeChallengeMethodIsMissing() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetClaim(Claims.Subject, "Bob le Bricoleur") .SetClaim(Claims.Private.CodeChallenge, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", CodeVerifier = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", GrantType = GrantTypes.AuthorizationCode }); }); Assert.Equal(SR.GetResourceString(SR.ID0268), exception.Message); } [Fact] public async Task ValidateTokenRequest_AuthorizationCodeCausesAnErrorWhenCodeChallengeMethodIsInvalid() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetClaim(Claims.Subject, "Bob le Bricoleur") .SetClaim(Claims.Private.CodeChallenge, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM") .SetClaim(Claims.Private.CodeChallengeMethod, "custom_code_challenge_method"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", CodeVerifier = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", GrantType = GrantTypes.AuthorizationCode }); }); Assert.Equal(SR.GetResourceString(SR.ID0045), exception.Message); } [Theory] [InlineData(CodeChallengeMethods.Plain, "challenge", "invalid_verifier")] [InlineData(CodeChallengeMethods.Sha256, "challenge", "invalid_verifier")] public async Task ValidateTokenRequest_AuthorizationCodeCausesAnErrorWhenCodeVerifierIsInvalid(string method, string challenge, string verifier) { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetClaim(Claims.Subject, "Bob le Bricoleur") .SetClaim(Claims.Private.CodeChallenge, challenge) .SetClaim(Claims.Private.CodeChallengeMethod, method); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", CodeVerifier = verifier, GrantType = GrantTypes.AuthorizationCode }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.FormatID2052(Parameters.CodeVerifier), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2052), response.ErrorUri); } [Theory] [InlineData(CodeChallengeMethods.Plain, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM")] [InlineData(CodeChallengeMethods.Sha256, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk")] public async Task ValidateTokenRequest_TokenRequestSucceedsWhenCodeVerifierIsValid(string method, string challenge, string verifier) { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetClaim(Claims.Subject, "Bob le Bricoleur") .SetClaim(Claims.Private.CodeChallenge, challenge) .SetClaim(Claims.Private.CodeChallengeMethod, method); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", CodeVerifier = verifier, GrantType = GrantTypes.AuthorizationCode }); // Assert Assert.NotNull(response.AccessToken); } [Fact] public async Task ValidateTokenRequest_RefreshTokenCausesAnErrorWhenScopeIsUnexpected() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.RegisterScopes(Scopes.Phone, Scopes.Profile); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetScopes(Enumerable.Empty()) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8", Scope = "profile phone" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.FormatID2074(Parameters.Scope), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2074), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RefreshTokenCausesAnErrorWhenScopeIsInvalid() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.RegisterScopes(Scopes.Phone, Scopes.Profile); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetScopes("profile", "email") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8", Scope = "profile phone" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.FormatID2052(Parameters.Scope), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2052), response.ErrorUri); } [Theory] [InlineData(GrantTypes.AuthorizationCode)] [InlineData(GrantTypes.ClientCredentials)] [InlineData(GrantTypes.DeviceCode)] [InlineData(GrantTypes.Password)] [InlineData(GrantTypes.RefreshToken)] [InlineData(GrantTypes.TokenExchange)] public async Task ValidateTokenRequest_RequestIsRejectedWhenFlowIsNotEnabled(string type) { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.Configure(options => options.GrantTypes.Remove(type)); options.Configure(options => options.ResponseTypes.Clear()); if (type is GrantTypes.DeviceCode) { options.Configure(options => options.DeviceAuthorizationEndpointUris.Clear()); } }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", type switch { GrantTypes.AuthorizationCode => new OpenIddictRequest { Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode }, GrantTypes.ClientCredentials => new OpenIddictRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", GrantType = GrantTypes.ClientCredentials }, GrantTypes.DeviceCode => new OpenIddictRequest { DeviceCode = "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS", GrantType = GrantTypes.DeviceCode }, GrantTypes.Password => new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }, GrantTypes.RefreshToken => new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }, _ => new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, SubjectToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", SubjectTokenType = TokenTypeIdentifiers.AccessToken } }); // Assert Assert.Equal(Errors.UnsupportedGrantType, response.Error); Assert.Equal(SR.FormatID2032(Parameters.GrantType), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2032), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RequestWithOfflineAccessScopeIsRejectedWhenRefreshTokenFlowIsDisabled() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.Configure(options => options.GrantTypes.Remove(GrantTypes.RefreshToken)); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w", Scope = Scopes.OfflineAccess }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2035(Scopes.OfflineAccess), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2035), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenUnregisteredScopeIsSpecified() { // Arrange await using var server = await CreateServerAsync(options => { options.Services.AddSingleton(CreateScopeManager(mock => { mock.Setup(manager => manager.FindByNamesAsync( It.Is>(scopes => scopes.Length == 1 && scopes[0] == "unregistered_scope"), It.IsAny())) .Returns(AsyncEnumerable.Empty()); })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w", Scope = "unregistered_scope" }); // Assert Assert.Equal(Errors.InvalidScope, response.Error); Assert.Equal(SR.FormatID2052(Parameters.Scope), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2052), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RequestIsValidatedWhenScopeRegisteredInOptionsIsSpecified() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.RegisterScopes("registered_scope"); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w", Scope = "registered_scope" }); // Assert Assert.Null(response.Error); Assert.Null(response.ErrorDescription); Assert.Null(response.ErrorUri); Assert.NotNull(response.AccessToken); } [Fact] public async Task ValidateTokenRequest_RequestIsValidatedWhenRegisteredScopeIsSpecified() { // Arrange var scope = new OpenIddictScope(); var manager = CreateScopeManager(mock => { mock.Setup(manager => manager.FindByNamesAsync( It.Is>(scopes => scopes.Length == 1 && scopes[0] == "scope_registered_in_database"), It.IsAny())) .Returns(new[] { scope }.ToAsyncEnumerable()); mock.Setup(manager => manager.GetNameAsync(scope, It.IsAny())) .ReturnsAsync("scope_registered_in_database"); }); await using var server = await CreateServerAsync(options => { options.RegisterScopes("scope_registered_in_options"); options.SetDeviceAuthorizationEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); options.Services.AddSingleton(manager); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w", Scope = "scope_registered_in_database scope_registered_in_options" }); // Assert Assert.Null(response.Error); Assert.Null(response.ErrorDescription); Assert.Null(response.ErrorUri); Assert.NotNull(response.AccessToken); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenUnregisteredAudienceIsSpecified() { // Arrange await using var server = await CreateServerAsync(); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { Audiences = ["unregistered_audience"], GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidTarget, response.Error); Assert.Equal(SR.FormatID2190(Parameters.Audience), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2190), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RequestIsValidatedWhenAudienceRegisteredInOptionsIsSpecified() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.RegisterAudiences("registered_audience"); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { Audiences = ["registered_audience"], GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Null(response.Error); Assert.Null(response.ErrorDescription); Assert.Null(response.ErrorUri); Assert.NotNull(response.AccessToken); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenUnregisteredResourceIsSpecified() { // Arrange await using var server = await CreateServerAsync(); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, Resources = ["urn:unregistered_resource"], SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidTarget, response.Error); Assert.Equal(SR.FormatID2190(Parameters.Resource), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2190), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RequestIsValidatedWhenResourceRegisteredInOptionsIsSpecified() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.RegisterResources("urn:registered_resource"); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, Resources = ["urn:registered_resource"], SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Null(response.Error); Assert.Null(response.ErrorDescription); Assert.Null(response.ErrorUri); Assert.NotNull(response.AccessToken); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenClientAssertionIsSpecifiedWithoutType() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientAssertion = "2YotnFZFEjr1zCsicMWpAA", ClientAssertionType = null, ClientId = "Fabrikam", GrantType = GrantTypes.ClientCredentials }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2037(Parameters.ClientAssertionType, Parameters.ClientAssertion), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2037), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenClientAssertionTypeIsSpecifiedWithoutAssertion() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientAssertion = null, ClientAssertionType = ClientAssertionTypes.JwtBearer, ClientId = "Fabrikam", GrantType = GrantTypes.ClientCredentials }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2037(Parameters.ClientAssertion, Parameters.ClientAssertionType), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2037), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenUnsupportedClientAssertionTypeIsSpecified() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.Configure(options => options.ClientAuthenticationMethods.Remove(ClientAuthenticationMethods.PrivateKeyJwt)); options.Configure(options => options.ClientAssertionTypes.Remove(ClientAssertionTypes.JwtBearer)); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientAssertion = "2YotnFZFEjr1zCsicMWpAA", ClientAssertionType = ClientAssertionTypes.JwtBearer, ClientId = "Fabrikam", GrantType = GrantTypes.ClientCredentials }); // Assert Assert.Equal(Errors.InvalidClient, response.Error); Assert.Equal(SR.FormatID2032(Parameters.ClientAssertionType), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2032), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenMultipleCredentialsAreSpecified() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientAssertion = "2YotnFZFEjr1zCsicMWpAA", ClientAssertionType = ClientAssertionTypes.JwtBearer, ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", GrantType = GrantTypes.ClientCredentials }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2087), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2087), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_ClientCredentialsRequestIsRejectedWhenCredentialsAreMissing() { // Arrange await using var server = await CreateServerAsync(options => options.EnableDegradedMode()); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientAssertion = null, ClientAssertionType = null, ClientId = "Fabrikam", ClientSecret = null, GrantType = GrantTypes.ClientCredentials }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2057(Parameters.ClientSecret, Parameters.ClientAssertion), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2057), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.Configure(options => options.AcceptAnonymousClients = false); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = null, GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Equal(Errors.InvalidClient, response.Error); Assert.Equal(SR.FormatID2029(Parameters.ClientId), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenClientCannotBeFound() { // Arrange var manager = CreateApplicationManager(mock => { mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(value: null); }); await using var server = await CreateServerAsync(options => { options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Equal(Errors.InvalidClient, response.Error); Assert.Equal(SR.FormatID2052(Parameters.ClientId), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2052), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.AtLeastOnce()); } [Fact] public async Task ValidateTokenRequest_ClientCredentialsRequestFromPublicClientIsRejected() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(mock => { mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); }); await using var server = await CreateServerAsync(options => { options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", GrantType = GrantTypes.ClientCredentials }); // Assert Assert.Equal(Errors.UnauthorizedClient, response.Error); Assert.Equal(SR.FormatID2043(Parameters.GrantType), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2043), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()), Times.Once()); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenEndpointPermissionIsNotGranted() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(mock => { mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasPermissionAsync(application, Permissions.Endpoints.Token, It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.Services.AddSingleton(manager); options.Configure(options => options.IgnoreEndpointPermissions = false); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Equal(Errors.UnauthorizedClient, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2063), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2063), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Permissions.Endpoints.Token, It.IsAny()), Times.Once()); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenGrantTypePermissionIsNotGranted() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(mock => { mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasPermissionAsync(application, Permissions.GrantTypes.Password, It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.Services.AddSingleton(manager); options.Configure(options => options.IgnoreGrantTypePermissions = false); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Equal(Errors.UnauthorizedClient, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2064), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2064), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Permissions.GrantTypes.Password, It.IsAny()), Times.Once()); } [Fact] public async Task ValidateTokenRequest_RequestWithOfflineAccessScopeIsRejectedWhenRefreshTokenPermissionIsNotGranted() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(mock => { mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasPermissionAsync(application, Permissions.GrantTypes.Password, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasPermissionAsync(application, Permissions.GrantTypes.RefreshToken, It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.Services.AddSingleton(manager); options.Configure(options => options.IgnoreGrantTypePermissions = false); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w", Scope = Scopes.OfflineAccess }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2065(Scopes.OfflineAccess), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2065), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Permissions.GrantTypes.RefreshToken, It.IsAny()), Times.Once()); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenScopePermissionIsNotGranted() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(mock => { mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Scope + Scopes.Profile, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Scope + Scopes.Email, It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.Services.AddSingleton(manager); options.RegisterScopes(Scopes.Email, Scopes.Profile); options.Configure(options => options.IgnoreScopePermissions = false); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w", Scope = "openid offline_access profile email" }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2051), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2051), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Scope + Scopes.OpenId, It.IsAny()), Times.Never()); Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Scope + Scopes.OfflineAccess, It.IsAny()), Times.Never()); Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Scope + Scopes.Profile, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Scope + Scopes.Email, It.IsAny()), Times.Once()); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenAudiencePermissionIsNotGranted() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(mock => { mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Audience + "Contoso", It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Audience + "Fabrikam", It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(manager); options.RegisterAudiences("Contoso", "Fabrikam"); options.Configure(options => options.IgnoreAudiencePermissions = false); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", GrantType = GrantTypes.TokenExchange, Audiences = ["Contoso", "Fabrikam"], SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2191), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2191), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Audience + "Contoso", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Audience + "Fabrikam", It.IsAny()), Times.Once()); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenResourcePermissionIsNotGranted() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(mock => { mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Resource + "urn:contoso", It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Resource + "urn:fabrikam", It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(manager); options.RegisterResources("urn:contoso", "urn:fabrikam"); options.Configure(options => options.IgnoreResourcePermissions = false); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", GrantType = GrantTypes.TokenExchange, Resources = ["urn:contoso", "urn:fabrikam"], SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2192), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2192), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Resource + "urn:contoso", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Permissions.Prefixes.Resource + "urn:fabrikam", It.IsAny()), Times.Once()); } [Fact] public async Task ValidateTokenRequest_RequestIsRejectedWhenCodeVerifierIsMissingWithPkceFeatureEnforced() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(mock => { mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasRequirementAsync(application, Requirements.Features.ProofKeyForCodeExchange, It.IsAny())) .ReturnsAsync(true); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(manager); options.SetDeviceAuthorizationEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", CodeVerifier = null, GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.Equal(Errors.InvalidRequest, response.Error); Assert.Equal(SR.FormatID2054(Parameters.CodeVerifier), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2054), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.HasRequirementAsync(application, Requirements.Features.ProofKeyForCodeExchange, It.IsAny()), Times.Once()); } [Fact] public async Task ValidateTokenRequest_RequestIsValidatedWhenCodeVerifierIsMissingWithPkceFeatureNotEnforced() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(mock => { mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasRequirementAsync(application, Requirements.Features.ProofKeyForCodeExchange, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.GetSettingsAsync(application, It.IsAny())) .ReturnsAsync(ImmutableDictionary.Create()); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(manager); options.SetDeviceAuthorizationEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", CodeVerifier = null, GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.NotNull(response.AccessToken); Mock.Get(manager).Verify(manager => manager.HasRequirementAsync(application, Requirements.Features.ProofKeyForCodeExchange, It.IsAny()), Times.Once()); } [Fact] public async Task ValidateTokenRequest_RequestIsValidatedWhenCodeVerifierIsPresentWithPkceFeatureEnforced() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(mock => { mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.HasRequirementAsync(application, Requirements.Features.ProofKeyForCodeExchange, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.GetSettingsAsync(application, It.IsAny())) .ReturnsAsync(ImmutableDictionary.Create()); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56") .SetClaim(Claims.Subject, "Bob le Bricoleur") .SetClaim(Claims.Private.CodeChallenge, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM") .SetClaim(Claims.Private.CodeChallengeMethod, CodeChallengeMethods.Sha256); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(manager); options.SetDeviceAuthorizationEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", CodeVerifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.NotNull(response.AccessToken); Mock.Get(manager).Verify(manager => manager.HasRequirementAsync(application, Requirements.Features.ProofKeyForCodeExchange, It.IsAny()), Times.Never()); } [Theory] [InlineData("custom_error", null, null)] [InlineData("custom_error", "custom_description", null)] [InlineData("custom_error", "custom_description", "custom_uri")] [InlineData(null, "custom_description", null)] [InlineData(null, "custom_description", "custom_uri")] [InlineData(null, null, "custom_uri")] [InlineData(null, null, null)] public async Task ValidateTokenRequest_AllowsRejectingRequest(string? error, string? description, string? uri) { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Reject(error, description, uri); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Equal(error ?? Errors.InvalidRequest, response.Error); Assert.Equal(description, response.ErrorDescription); Assert.Equal(uri, response.ErrorUri); } [Fact] public async Task ValidateTokenRequest_AllowsHandlingResponse() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Transaction.SetProperty("custom_response", new { name = "Bob le Bricoleur" }); context.HandleRequest(); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Equal("Bob le Bricoleur", (string?) response["name"]); } [Fact] public async Task ValidateTokenRequest_AllowsSkippingHandler() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Equal("Bob le Magnifique", (string?) response["name"]); } [Fact] public async Task HandleTokenRequest_AuthorizationCodeRevocationIsIgnoredWhenTokenStorageIsDisabled() { // Arrange await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateApplicationManager(mock => { var application = new OpenIddictApplication(); mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetSettingsAsync(application, It.IsAny())) .ReturnsAsync(ImmutableDictionary.Create()); })); options.SetDeviceAuthorizationEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.NotNull(response.AccessToken); } [Fact] public async Task HandleTokenRequest_RefreshTokenRevocationIsIgnoredWhenTokenStorageIsDisabled() { // Arrange await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.SetDeviceAuthorizationEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.NotNull(response.AccessToken); } [Fact] public async Task HandleTokenRequest_SubjectTokenRevocationIsIgnoredWhenTokenStorageIsDisabled() { // Arrange await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.SetDeviceAuthorizationEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.NotNull(response.AccessToken); } [Fact] public async Task HandleTokenRequest_ActorTokenRevocationIsIgnoredWhenTokenStorageIsDisabled() { // Arrange await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { if (string.Equals(context.Token, "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", StringComparison.Ordinal)) { Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetTokenId("E2894547-277E-4E09-A0C0-0A0631B54052") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; } Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.SetDeviceAuthorizationEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.NotNull(response.AccessToken); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationCodeIsUnknown() { // Arrange var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) .ReturnsAsync(value: null); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateApplicationManager(mock => { var application = new OpenIddictApplication(); mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2001), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2001), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenRefreshTokenIsUnknown() { // Arrange var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(value: null); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2003), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2003), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenSubjectTokenIsUnknown() { // Arrange var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(value: null); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2003), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2003), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenActorTokenIsUnknown() { // Arrange var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(value: null); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { if (string.Equals(context.Token, "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", StringComparison.Ordinal)) { Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetTokenId("E2894547-277E-4E09-A0C0-0A0631B54052") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; } Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2003), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2003), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationCodeIsAlreadyRedeemed() { // Arrange var token = new OpenIddictToken(); var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.Private.AuthorizationCode); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateApplicationManager(mock => { var application = new OpenIddictApplication(); mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2010), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2010), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.GetRedemptionDateAsync(token, It.IsAny()), Times.Never()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenRefreshTokenIsAlreadyRedeemedAndLeewayIsNull() { // Arrange var token = new OpenIddictToken(); var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetRedemptionDateAsync(token, It.IsAny())) .ReturnsAsync(TimeProvider.System.GetUtcNow()); }); await using var server = await CreateServerAsync(options => { options.SetRefreshTokenReuseLeeway(leeway: null); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2012), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2012), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.GetRedemptionDateAsync(token, It.IsAny()), Times.Never()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenRefreshTokenIsAlreadyRedeemedAndCannotBeReused() { // Arrange var token = new OpenIddictToken(); var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetRedemptionDateAsync(token, It.IsAny())) .ReturnsAsync(TimeProvider.System.GetUtcNow() - TimeSpan.FromMinutes(1)); }); await using var server = await CreateServerAsync(options => { options.SetRefreshTokenReuseLeeway(TimeSpan.FromSeconds(5)); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2012), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2012), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.GetRedemptionDateAsync(token, It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RequestIsValidatedWhenRefreshTokenIsAlreadyRedeemedAndCanBeReused() { // Arrange var token = new OpenIddictToken(); var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetRedemptionDateAsync(token, It.IsAny())) .ReturnsAsync(TimeProvider.System.GetUtcNow() - TimeSpan.FromMinutes(1)); mock.Setup(manager => manager.CreateAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new OpenIddictToken()); }); await using var server = await CreateServerAsync(options => { options.SetRefreshTokenReuseLeeway(TimeSpan.FromMinutes(5)); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.NotNull(response.AccessToken); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.GetRedemptionDateAsync(token, It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RevokesTokensWhenAuthorizationCodeIsAlreadyRedeemed() { // Arrange ImmutableArray tokens = [new(), new(), new()]; var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) .ReturnsAsync(tokens[0]); mock.Setup(manager => manager.GetIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); mock.Setup(manager => manager.GetIdAsync(tokens[1], It.IsAny())) .ReturnsAsync("47468A64-C9A7-49C7-939C-19CC0F5DD166"); mock.Setup(manager => manager.GetIdAsync(tokens[2], It.IsAny())) .ReturnsAsync("3BEA7A94-5ADA-49AF-9F41-8AB6156E31A8"); mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); mock.Setup(manager => manager.GetTypeAsync(tokens[0], It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.Private.AuthorizationCode); mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.FindByAuthorizationIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .Returns(tokens.ToAsyncEnumerable()); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateApplicationManager(mock => { var application = new OpenIddictApplication(); mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2010), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2010), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.RevokeByAuthorizationIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RevokesTokensWhenRefreshTokenIsAlreadyRedeemedAndLeewayIsNull() { // Arrange ImmutableArray tokens = [new(), new(), new()]; var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(tokens[0]); mock.Setup(manager => manager.GetIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetIdAsync(tokens[1], It.IsAny())) .ReturnsAsync("47468A64-C9A7-49C7-939C-19CC0F5DD166"); mock.Setup(manager => manager.GetIdAsync(tokens[2], It.IsAny())) .ReturnsAsync("3BEA7A94-5ADA-49AF-9F41-8AB6156E31A8"); mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); mock.Setup(manager => manager.GetTypeAsync(tokens[0], It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetRedemptionDateAsync(tokens[0], It.IsAny())) .ReturnsAsync(TimeProvider.System.GetUtcNow()); mock.Setup(manager => manager.FindByAuthorizationIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .Returns(tokens.ToAsyncEnumerable()); }); await using var server = await CreateServerAsync(options => { options.SetRefreshTokenReuseLeeway(leeway: null); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetPresenters("Fabrikam") .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2012), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2012), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.RevokeByAuthorizationIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RevokesTokensWhenRefreshTokenIsAlreadyRedeemedAndCannotBeReused() { // Arrange ImmutableArray tokens = [new(), new(), new()]; var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(tokens[0]); mock.Setup(manager => manager.GetIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetIdAsync(tokens[1], It.IsAny())) .ReturnsAsync("47468A64-C9A7-49C7-939C-19CC0F5DD166"); mock.Setup(manager => manager.GetIdAsync(tokens[2], It.IsAny())) .ReturnsAsync("3BEA7A94-5ADA-49AF-9F41-8AB6156E31A8"); mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); mock.Setup(manager => manager.GetTypeAsync(tokens[0], It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetRedemptionDateAsync(tokens[0], It.IsAny())) .ReturnsAsync(TimeProvider.System.GetUtcNow() - TimeSpan.FromMinutes(1)); mock.Setup(manager => manager.FindByAuthorizationIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .Returns(tokens.ToAsyncEnumerable()); }); await using var server = await CreateServerAsync(options => { options.SetRefreshTokenReuseLeeway(TimeSpan.FromSeconds(5)); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetPresenters("Fabrikam") .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2012), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2012), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.RevokeByAuthorizationIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_DoesNotRevokeTokensWhenRefreshTokenIsAlreadyRedeemedAndCanBeReused() { // Arrange ImmutableArray tokens = [new(), new(), new()]; var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(tokens[0]); mock.Setup(manager => manager.GetIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetIdAsync(tokens[1], It.IsAny())) .ReturnsAsync("47468A64-C9A7-49C7-939C-19CC0F5DD166"); mock.Setup(manager => manager.GetIdAsync(tokens[2], It.IsAny())) .ReturnsAsync("3BEA7A94-5ADA-49AF-9F41-8AB6156E31A8"); mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); mock.Setup(manager => manager.GetTypeAsync(tokens[0], It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetRedemptionDateAsync(tokens[0], It.IsAny())) .ReturnsAsync(TimeProvider.System.GetUtcNow() - TimeSpan.FromMinutes(1)); mock.Setup(manager => manager.FindByAuthorizationIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .Returns(tokens.ToAsyncEnumerable()); mock.Setup(manager => manager.CreateAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new OpenIddictToken()); }); await using var server = await CreateServerAsync(options => { options.SetRefreshTokenReuseLeeway(TimeSpan.FromMinutes(5)); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(manager); options.Services.AddSingleton(CreateAuthorizationManager(mock => { var authorization = new OpenIddictAuthorization(); mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(authorization); mock.Setup(manager => manager.HasStatusAsync(authorization, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.NotNull(response.AccessToken); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.TryRevokeAsync(tokens[0], It.IsAny()), Times.Never()); Mock.Get(manager).Verify(manager => manager.TryRevokeAsync(tokens[1], It.IsAny()), Times.Never()); Mock.Get(manager).Verify(manager => manager.TryRevokeAsync(tokens[2], It.IsAny()), Times.Never()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationCodeIsInvalid() { // Arrange var token = new OpenIddictToken(); var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.Private.AuthorizationCode); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateApplicationManager(mock => { var application = new OpenIddictApplication(); mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2016), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2016), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenRefreshTokenIsInvalid() { // Arrange var token = new OpenIddictToken(); var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2018), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2018), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenSubjectTokenIsInvalid() { // Arrange var token = new OpenIddictToken(); var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2018), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2018), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenActorTokenIsInvalid() { // Arrange ImmutableArray tokens = [new(), new()]; var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(tokens[0]); mock.Setup(manager => manager.GetIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(tokens[0], It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.FindByIdAsync("E2894547-277E-4E09-A0C0-0A0631B54052", It.IsAny())) .ReturnsAsync(tokens[1]); mock.Setup(manager => manager.GetIdAsync(tokens[1], It.IsAny())) .ReturnsAsync("E2894547-277E-4E09-A0C0-0A0631B54052"); mock.Setup(manager => manager.GetTypeAsync(tokens[1], It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.AccessToken); mock.Setup(manager => manager.HasStatusAsync(tokens[1], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(tokens[1], Statuses.Valid, It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { if (string.Equals(context.Token, "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", StringComparison.Ordinal)) { Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetTokenId("E2894547-277E-4E09-A0C0-0A0631B54052") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; } Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2019), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2019), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("E2894547-277E-4E09-A0C0-0A0631B54052", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(tokens[1], Statuses.Valid, It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_AuthorizationAssociatedWithCodeIsIgnoredWhenAuthorizationStorageIsDisabled() { // Arrange var manager = CreateAuthorizationManager(mock => { mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(new OpenIddictAuthorization()); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateApplicationManager(mock => { var application = new OpenIddictApplication(); mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetSettingsAsync(application, It.IsAny())) .ReturnsAsync(ImmutableDictionary.Create()); })); options.Services.AddSingleton(CreateTokenManager(mock => { var token = new OpenIddictToken(); mock.Setup(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.Private.AuthorizationCode); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(token, It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); mock.Setup(manager => manager.TryRedeemAsync(token, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.CreateAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new OpenIddictToken()); })); options.Services.AddSingleton(manager); options.DisableAuthorizationStorage(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.NotNull(response.AccessToken); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Never()); } [Fact] public async Task HandleTokenRequest_AuthorizationAssociatedWithRefreshTokenIsIgnoredWhenAuthorizationStorageIsDisabled() { // Arrange var authorization = new OpenIddictAuthorization(); var manager = CreateAuthorizationManager(mock => { mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(new OpenIddictAuthorization()); }); await using var server = await CreateServerAsync(options => { options.DisableRollingRefreshTokens(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateTokenManager(mock => { var token = new OpenIddictToken(); mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(token, It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); mock.Setup(manager => manager.CreateAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new OpenIddictToken()); })); options.Services.AddSingleton(manager); options.DisableAuthorizationStorage(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.NotNull(response.AccessToken); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Never()); } [Fact] public async Task HandleTokenRequest_AuthorizationAssociatedWithSubjectTokenIsIgnoredWhenAuthorizationStorageIsDisabled() { // Arrange var authorization = new OpenIddictAuthorization(); var manager = CreateAuthorizationManager(mock => { mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(new OpenIddictAuthorization()); }); await using var server = await CreateServerAsync(options => { options.DisableRollingRefreshTokens(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(CreateTokenManager(mock => { var token = new OpenIddictToken(); mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(token, It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); mock.Setup(manager => manager.CreateAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new OpenIddictToken()); })); options.Services.AddSingleton(manager); options.DisableAuthorizationStorage(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.NotNull(response.AccessToken); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Never()); } [Fact] public async Task HandleTokenRequest_AuthorizationAssociatedWithActorTokenIsIgnoredWhenAuthorizationStorageIsDisabled() { // Arrange var authorization = new OpenIddictAuthorization(); var manager = CreateAuthorizationManager(mock => { mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(new OpenIddictAuthorization()); }); await using var server = await CreateServerAsync(options => { options.DisableRollingRefreshTokens(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { if (string.Equals(context.Token, "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", StringComparison.Ordinal)) { Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetTokenId("E2894547-277E-4E09-A0C0-0A0631B54052") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; } Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(CreateTokenManager(mock => { ImmutableArray tokens = [new(), new()]; mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(tokens[0]); mock.Setup(manager => manager.GetIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(tokens[0], It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); mock.Setup(manager => manager.FindByIdAsync("E2894547-277E-4E09-A0C0-0A0631B54052", It.IsAny())) .ReturnsAsync(tokens[1]); mock.Setup(manager => manager.GetIdAsync(tokens[1], It.IsAny())) .ReturnsAsync("E2894547-277E-4E09-A0C0-0A0631B54052"); mock.Setup(manager => manager.GetTypeAsync(tokens[1], It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.AccessToken); mock.Setup(manager => manager.HasStatusAsync(tokens[1], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(tokens[1], Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[1], It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); mock.Setup(manager => manager.CreateAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new OpenIddictToken()); })); options.Services.AddSingleton(manager); options.DisableAuthorizationStorage(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.NotNull(response.AccessToken); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Never()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationAssociatedWithAuthorizationCodeCannotBeFound() { // Arrange var manager = CreateAuthorizationManager(mock => { mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(value: null); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateApplicationManager(mock => { var application = new OpenIddictApplication(); mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); })); options.Services.AddSingleton(CreateTokenManager(mock => { var token = new OpenIddictToken(); mock.Setup(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.Private.AuthorizationCode); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(token, It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2020), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2020), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationAssociatedWithAuthorizationCodeIsInvalid() { // Arrange var authorization = new OpenIddictAuthorization(); var manager = CreateAuthorizationManager(mock => { mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(authorization); mock.Setup(manager => manager.HasStatusAsync(authorization, Statuses.Valid, It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateApplicationManager(mock => { var application = new OpenIddictApplication(); mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); })); options.Services.AddSingleton(CreateTokenManager(mock => { var token = new OpenIddictToken(); mock.Setup(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.Private.AuthorizationCode); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(token, It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2020), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2020), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(authorization, Statuses.Valid, It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationAssociatedWithRefreshTokenCannotBeFound() { // Arrange var manager = CreateAuthorizationManager(mock => { mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(value: null); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateTokenManager(mock => { var token = new OpenIddictToken(); mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(token, It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2022), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2022), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationAssociatedWithRefreshTokenIsInvalid() { // Arrange var authorization = new OpenIddictAuthorization(); var manager = CreateAuthorizationManager(mock => { mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(authorization); mock.Setup(manager => manager.HasStatusAsync(authorization, Statuses.Valid, It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateTokenManager(mock => { var token = new OpenIddictToken(); mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(token, It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2022), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2022), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(authorization, Statuses.Valid, It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationAssociatedWithSubjectTokenCannotBeFound() { // Arrange var manager = CreateAuthorizationManager(mock => { mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(value: null); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(CreateTokenManager(mock => { var token = new OpenIddictToken(); mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(token, It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2022), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2022), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationAssociatedWithSubjectTokenIsInvalid() { // Arrange var authorization = new OpenIddictAuthorization(); var manager = CreateAuthorizationManager(mock => { mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(authorization); mock.Setup(manager => manager.HasStatusAsync(authorization, Statuses.Valid, It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("8xLOxBtZp8", context.Token); Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(CreateTokenManager(mock => { var token = new OpenIddictToken(); mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(token, It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2022), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2022), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(authorization, Statuses.Valid, It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationAssociatedWithActorTokenCannotBeFound() { // Arrange var authorization = new OpenIddictAuthorization(); var manager = CreateAuthorizationManager(mock => { mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(authorization); mock.Setup(manager => manager.HasStatusAsync(authorization, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.FindByIdAsync("224E3451-A3D4-48C3-A189-2D95B97C212A", It.IsAny())) .ReturnsAsync(value: null); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { if (string.Equals(context.Token, "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", StringComparison.Ordinal)) { Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetTokenId("E2894547-277E-4E09-A0C0-0A0631B54052") .SetAuthorizationId("224E3451-A3D4-48C3-A189-2D95B97C212A") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; } Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(CreateTokenManager(mock => { ImmutableArray tokens = [new(), new()]; mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(tokens[0]); mock.Setup(manager => manager.GetIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(tokens[0], It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); mock.Setup(manager => manager.FindByIdAsync("E2894547-277E-4E09-A0C0-0A0631B54052", It.IsAny())) .ReturnsAsync(tokens[1]); mock.Setup(manager => manager.GetIdAsync(tokens[1], It.IsAny())) .ReturnsAsync("E2894547-277E-4E09-A0C0-0A0631B54052"); mock.Setup(manager => manager.GetTypeAsync(tokens[1], It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.AccessToken); mock.Setup(manager => manager.HasStatusAsync(tokens[1], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(tokens[1], Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[1], It.IsAny())) .ReturnsAsync("224E3451-A3D4-48C3-A189-2D95B97C212A"); })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2023), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2023), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("224E3451-A3D4-48C3-A189-2D95B97C212A", It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationAssociatedWithActorTokenIsInvalid() { // Arrange ImmutableArray authorizations = [new(), new()]; var manager = CreateAuthorizationManager(mock => { mock.Setup(manager => manager.FindByIdAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0", It.IsAny())) .ReturnsAsync(authorizations[0]); mock.Setup(manager => manager.HasStatusAsync(authorizations[0], Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.FindByIdAsync("224E3451-A3D4-48C3-A189-2D95B97C212A", It.IsAny())) .ReturnsAsync(authorizations[1]); mock.Setup(manager => manager.HasStatusAsync(authorizations[1], Statuses.Valid, It.IsAny())) .ReturnsAsync(false); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { if (string.Equals(context.Token, "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", StringComparison.Ordinal)) { Assert.Equal([TokenTypeIdentifiers.AccessToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.AccessToken) .SetTokenId("E2894547-277E-4E09-A0C0-0A0631B54052") .SetAuthorizationId("224E3451-A3D4-48C3-A189-2D95B97C212A") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; } Assert.Equal([TokenTypeIdentifiers.RefreshToken], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.RefreshToken) .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") .SetAuthorizationId("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.Services.AddSingleton(CreateTokenManager(mock => { ImmutableArray tokens = [new(), new()]; mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(tokens[0]); mock.Setup(manager => manager.GetIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); mock.Setup(manager => manager.GetTypeAsync(tokens[0], It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.RefreshToken); mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); mock.Setup(manager => manager.FindByIdAsync("E2894547-277E-4E09-A0C0-0A0631B54052", It.IsAny())) .ReturnsAsync(tokens[1]); mock.Setup(manager => manager.GetIdAsync(tokens[1], It.IsAny())) .ReturnsAsync("E2894547-277E-4E09-A0C0-0A0631B54052"); mock.Setup(manager => manager.GetTypeAsync(tokens[1], It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.AccessToken); mock.Setup(manager => manager.HasStatusAsync(tokens[1], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(tokens[1], Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[1], It.IsAny())) .ReturnsAsync("224E3451-A3D4-48C3-A189-2D95B97C212A"); })); options.Services.AddSingleton(manager); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ActorToken = "accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC", ActorTokenType = TokenTypeIdentifiers.AccessToken, GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2023), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2023), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.FindByIdAsync("224E3451-A3D4-48C3-A189-2D95B97C212A", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(manager => manager.HasStatusAsync(authorizations[1], Statuses.Valid, It.IsAny()), Times.Once()); } [Fact] public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationCodeCannotBeRedeemed() { // Arrange var token = new OpenIddictToken(); var manager = CreateTokenManager(mock => { mock.Setup(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(TokenTypeIdentifiers.Private.AuthorizationCode); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.TryRedeemAsync(token, It.IsAny())) .ReturnsAsync(false); mock.Setup(manager => manager.CreateAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new OpenIddictToken()); }); await using var server = await CreateServerAsync(options => { options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); Assert.Equal([TokenTypeIdentifiers.Private.AuthorizationCode], context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeIdentifiers.Private.AuthorizationCode) .SetPresenters("Fabrikam") .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateApplicationManager(mock => { var application = new OpenIddictApplication(); mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) .ReturnsAsync(true); })); options.Services.AddSingleton(manager); options.DisableAuthorizationStorage(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { ClientId = "Fabrikam", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode, RedirectUri = "http://www.fabrikam.com/path" }); // Assert Assert.Equal(Errors.InvalidGrant, response.Error); Assert.Equal(SR.GetResourceString(SR.ID2010), response.ErrorDescription); Assert.Equal(SR.FormatID8000(SR.ID2010), response.ErrorUri); Mock.Get(manager).Verify(manager => manager.TryRedeemAsync(token, It.IsAny()), Times.Once()); } [Theory] [InlineData(GrantTypes.AuthorizationCode)] [InlineData(GrantTypes.ClientCredentials)] [InlineData(GrantTypes.DeviceCode)] [InlineData(GrantTypes.Password)] [InlineData(GrantTypes.RefreshToken)] [InlineData(GrantTypes.TokenExchange)] [InlineData("urn:ietf:params:oauth:grant-type:custom_grant")] public async Task HandleTokenRequest_RequestsAreSuccessfullyHandled(string type) { // Arrange var manager = CreateTokenManager(mock => { var token = new OpenIddictToken(); mock.Setup(manager => manager.FindByIdAsync("0270F515-C5B1-4FBF-B673-D7CAF7CCDABC", It.IsAny())) .ReturnsAsync(token); mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("0270F515-C5B1-4FBF-B673-D7CAF7CCDABC"); mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) .ReturnsAsync(type switch { GrantTypes.AuthorizationCode => TokenTypeIdentifiers.Private.AuthorizationCode, GrantTypes.DeviceCode => TokenTypeIdentifiers.Private.DeviceCode, GrantTypes.RefreshToken => TokenTypeIdentifiers.RefreshToken, GrantTypes.TokenExchange => TokenTypeIdentifiers.RefreshToken, _ => null }); mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetAuthorizationIdAsync(token, It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); mock.Setup(manager => manager.TryRedeemAsync(token, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.CreateAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new OpenIddictToken()); }); await using var server = await CreateServerAsync(options => { options.DisableRollingRefreshTokens(); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(type switch { GrantTypes.AuthorizationCode => TokenTypeIdentifiers.Private.AuthorizationCode, GrantTypes.DeviceCode => TokenTypeIdentifiers.Private.DeviceCode, GrantTypes.RefreshToken => TokenTypeIdentifiers.RefreshToken, GrantTypes.TokenExchange => TokenTypeIdentifiers.RefreshToken, _ => null }) .SetPresenters("Fabrikam") .SetTokenId("0270F515-C5B1-4FBF-B673-D7CAF7CCDABC") .SetClaim(Claims.Subject, "Bob le Bricoleur"); return ValueTask.CompletedTask; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.Services.AddSingleton(CreateApplicationManager(mock => { var application = new OpenIddictApplication(); mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny())) .ReturnsAsync(true); mock.Setup(manager => manager.GetSettingsAsync(application, It.IsAny())) .ReturnsAsync(ImmutableDictionary.Create()); })); options.Services.AddSingleton(manager); options.AllowCustomFlow("urn:ietf:params:oauth:grant-type:custom_grant"); options.DisableAuthorizationStorage(); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", type switch { GrantTypes.AuthorizationCode => new OpenIddictRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", Code = "SplxlOBeZQQYbYS6WxSbIA", GrantType = GrantTypes.AuthorizationCode }, GrantTypes.ClientCredentials => new OpenIddictRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", GrantType = GrantTypes.ClientCredentials }, GrantTypes.DeviceCode => new OpenIddictRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", DeviceCode = "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS", GrantType = GrantTypes.DeviceCode }, GrantTypes.Password => new OpenIddictRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }, GrantTypes.RefreshToken => new OpenIddictRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", GrantType = GrantTypes.RefreshToken, RefreshToken = "8xLOxBtZp8" }, GrantTypes.TokenExchange => new OpenIddictRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", GrantType = GrantTypes.TokenExchange, SubjectToken = "8xLOxBtZp8", SubjectTokenType = TokenTypeIdentifiers.RefreshToken }, _ => new OpenIddictRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", GrantType = "urn:ietf:params:oauth:grant-type:custom_grant" } }); // Assert Assert.NotNull(response.AccessToken); } [Theory] [InlineData("custom_error", null, null)] [InlineData("custom_error", "custom_description", null)] [InlineData("custom_error", "custom_description", "custom_uri")] [InlineData(null, "custom_description", null)] [InlineData(null, "custom_description", "custom_uri")] [InlineData(null, null, "custom_uri")] [InlineData(null, null, null)] public async Task HandleTokenRequest_AllowsRejectingRequest(string? error, string? description, string? uri) { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Reject(error, description, uri); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Equal(error ?? Errors.InvalidGrant, response.Error); Assert.Equal(description, response.ErrorDescription); Assert.Equal(uri, response.ErrorUri); } [Fact] public async Task HandleTokenRequest_AllowsHandlingResponse() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Transaction.SetProperty("custom_response", new { name = "Bob le Bricoleur" }); context.HandleRequest(); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Equal("Bob le Bricoleur", (string?) response["name"]); } [Fact] public async Task HandleTokenRequest_AllowsSkippingHandler() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Equal("Bob le Magnifique", (string?) response["name"]); } [Fact] public async Task HandleTokenRequest_ResponseContainsCustomParameters() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); context.Parameters["custom_parameter"] = "custom_value"; context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Null(response.Error); Assert.Null(response.ErrorDescription); Assert.Null(response.ErrorUri); Assert.NotNull(response.AccessToken); Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } [Fact] public async Task ApplyTokenResponse_AllowsHandlingResponse() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Transaction.SetProperty("custom_response", new { name = "Bob le Bricoleur" }); context.HandleRequest(); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Equal("Bob le Bricoleur", (string?) response["name"]); } [Fact] public async Task ApplyTokenResponse_ResponseContainsCustomParameters() { // Arrange await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return ValueTask.CompletedTask; })); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.Response["custom_parameter"] = "custom_value"; context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return ValueTask.CompletedTask; })); }); await using var client = await server.CreateClientAsync(); // Act var response = await client.PostAsync("/connect/token", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w" }); // Assert Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } }