/* * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) * See https://github.com/openiddict/openiddict-core for more information concerning * the license and the contributors participating to this project. */ using System; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; using OpenIddict.Abstractions; using OpenIddict.Core; using Xunit; using static OpenIddict.Abstractions.OpenIddictConstants; using static OpenIddict.Server.OpenIddictServerEvents; using static OpenIddict.Server.OpenIddictServerHandlers; namespace OpenIddict.Server.FunctionalTests { public abstract partial class OpenIddictServerIntegrationTests { [Fact] public async Task ProcessAuthentication_UnknownEndpointCausesAnException() { // Arrange var client = CreateClient(options => options.EnableDegradedMode()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.PostAsync("/authenticate", new OpenIddictRequest()); }); Assert.Equal(new StringBuilder() .AppendLine("An identity cannot be extracted from this request.") .Append("This generally indicates that the OpenIddict server stack was asked ") .AppendLine("to validate a token for an endpoint it doesn't manage.") .Append("To validate tokens received by custom API endpoints, ") .Append("the OpenIddict validation services must be used instead.") .ToString(), exception.Message); } [Fact] public async Task ProcessAuthentication_InvalidEndpointCausesAnException() { // Arrange var client = CreateClient(options => { options.EnableDegradedMode(); options.SetConfigurationEndpointUris("/authenticate"); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return default; })); }); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/authenticate"); }); Assert.Equal(new StringBuilder() .AppendLine("An identity cannot be extracted from this request.") .Append("This generally indicates that the OpenIddict server stack was asked ") .AppendLine("to validate a token for an endpoint it doesn't manage.") .Append("To validate tokens received by custom API endpoints, ") .Append("the OpenIddict validation services must be used instead.") .ToString(), exception.Message); } [Fact] public async Task ProcessAuthentication_UnsupportedGrantTypeThrowsAnException() { // Arrange var client = CreateClient(options => { options.EnableDegradedMode(); options.SetTokenEndpointUris("/authenticate"); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return default; })); }); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.PostAsync("/authenticate", new OpenIddictRequest { GrantType = GrantTypes.Password, Username = "johndoe", Password = "A3ddj3w", }); }); Assert.Equal(new StringBuilder() .AppendLine("An identity cannot be extracted from this token request.") .Append("This generally indicates that the OpenIddict server stack was asked ") .AppendLine("to validate a token for an invalid grant type (e.g password).") .ToString(), exception.Message); } [Fact] public async Task ProcessAuthentication_MissingIdTokenHintReturnsNull() { // Arrange var client = CreateClient(options => { options.EnableDegradedMode(); options.SetLogoutEndpointUris("/authenticate"); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return default; })); }); // Act var response = await client.GetAsync("/authenticate", new OpenIddictRequest { IdTokenHint = null }); // Assert Assert.Null((string) response[Claims.Subject]); } [Fact] public async Task ProcessAuthentication_InvalidIdTokenHintReturnsNull() { // Arrange var client = CreateClient(options => { options.EnableDegradedMode(); options.SetLogoutEndpointUris("/authenticate"); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return default; })); }); // Act var response = await client.GetAsync("/authenticate", new OpenIddictRequest { IdTokenHint = "38323A4B-6CB2-41B8-B457-1951987CB383" }); // Assert Assert.Null((string) response[Claims.Subject]); } [Fact] public async Task ProcessAuthentication_ValidIdTokenHintReturnsExpectedIdentity() { // Arrange var client = CreateClient(options => { options.EnableDegradedMode(); options.SetLogoutEndpointUris("/authenticate"); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return default; })); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("id_token", context.Token); Assert.Equal(TokenTypeHints.IdToken, context.TokenType); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return default; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); // Act var response = await client.GetAsync("/authenticate", new OpenIddictRequest { IdTokenHint = "id_token" }); // Assert Assert.Equal("Bob le Magnifique", (string) response[Claims.Subject]); } [Fact] public async Task ProcessAuthentication_MissingAuthorizationCodeReturnsNull() { // Arrange var client = CreateClient(options => { options.EnableDegradedMode(); options.SetTokenEndpointUris("/authenticate"); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return default; })); }); // Act var response = await client.PostAsync("/authenticate", new OpenIddictRequest { ClientId = "Fabrikam", Code = null, GrantType = GrantTypes.AuthorizationCode }); // Assert Assert.Null((string) response[Claims.Subject]); } [Fact] public async Task ProcessAuthentication_InvalidAuthorizationCodeReturnsNull() { // Arrange var client = CreateClient(options => { options.EnableDegradedMode(); options.SetTokenEndpointUris("/authenticate"); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return default; })); }); // Act var response = await client.PostAsync("/authenticate", new OpenIddictRequest { ClientId = "Fabrikam", Code = "38323A4B-6CB2-41B8-B457-1951987CB383", GrantType = GrantTypes.AuthorizationCode }); // Assert Assert.Null((string) response[Claims.Subject]); } [Fact] public async Task ProcessAuthentication_ValidAuthorizationCodeReturnsExpectedIdentity() { // Arrange var client = CreateClient(options => { options.EnableDegradedMode(); options.SetTokenEndpointUris("/authenticate"); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return default; })); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("authorization_code", context.Token); Assert.Equal(TokenTypeHints.AuthorizationCode, context.TokenType); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique") .SetPresenters("Fabrikam"); return default; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); // Act var response = await client.PostAsync("/authenticate", new OpenIddictRequest { ClientId = "Fabrikam", Code = "authorization_code", GrantType = GrantTypes.AuthorizationCode }); // Assert Assert.Equal("Bob le Magnifique", (string) response[Claims.Subject]); } [Fact] public async Task ProcessAuthentication_MissingRefreshTokenReturnsNull() { // Arrange var client = CreateClient(options => { options.EnableDegradedMode(); options.SetTokenEndpointUris("/authenticate"); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return default; })); }); // Act var response = await client.PostAsync("/authenticate", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = null }); // Assert Assert.Null((string) response[Claims.Subject]); } [Fact] public async Task ProcessAuthentication_InvalidRefreshTokenReturnsNull() { // Arrange var client = CreateClient(options => { options.EnableDegradedMode(); options.SetTokenEndpointUris("/authenticate"); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return default; })); }); // Act var response = await client.PostAsync("/authenticate", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "38323A4B-6CB2-41B8-B457-1951987CB383" }); // Assert Assert.Null((string) response[Claims.Subject]); } [Fact] public async Task ProcessAuthentication_ValidRefreshTokenReturnsExpectedIdentity() { // Arrange var client = CreateClient(options => { options.EnableDegradedMode(); options.SetTokenEndpointUris("/authenticate"); options.AddEventHandler(builder => builder.UseInlineHandler(context => { context.SkipRequest(); return default; })); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { Assert.Equal("refresh_token", context.Token); Assert.Equal(TokenTypeHints.RefreshToken, context.TokenType); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetClaim(Claims.Subject, "Bob le Magnifique"); return default; }); builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); }); }); // Act var response = await client.PostAsync("/authenticate", new OpenIddictRequest { GrantType = GrantTypes.RefreshToken, RefreshToken = "refresh_token" }); // Assert Assert.Equal("Bob le Magnifique", (string) response[Claims.Subject]); } protected virtual void ConfigureServices(IServiceCollection services) { services.AddOpenIddict() .AddCore(options => { options.SetDefaultApplicationEntity() .SetDefaultAuthorizationEntity() .SetDefaultScopeEntity() .SetDefaultTokenEntity(); options.Services.AddSingleton(CreateApplicationManager()) .AddSingleton(CreateAuthorizationManager()) .AddSingleton(CreateScopeManager()) .AddSingleton(CreateTokenManager()); }) .AddServer(options => { // Enable the tested endpoints. options.SetAuthorizationEndpointUris("/connect/authorize") .SetConfigurationEndpointUris("/.well-known/openid-configuration") .SetCryptographyEndpointUris("/.well-known/jwks") .SetIntrospectionEndpointUris("/connect/introspect") .SetLogoutEndpointUris("/connect/logout") .SetRevocationEndpointUris("/connect/revoke") .SetTokenEndpointUris("/connect/token") .SetUserinfoEndpointUris("/connect/userinfo"); options.AllowAuthorizationCodeFlow() .AllowClientCredentialsFlow() .AllowImplicitFlow() .AllowPasswordFlow() .AllowRefreshTokenFlow(); // Accept anonymous clients by default. options.AcceptAnonymousClients(); // Disable permission enforcement by default. options.IgnoreEndpointPermissions() .IgnoreGrantTypePermissions() .IgnoreScopePermissions(); options.AddSigningCertificate( assembly: typeof(OpenIddictServerIntegrationTests).Assembly, resource: "OpenIddict.Server.IntegrationTests.Certificate.pfx", password: "Owin.Security.OpenIdConnect.Server"); options.AddEncryptionCertificate( assembly: typeof(OpenIddictServerIntegrationTests).Assembly, resource: "OpenIddict.Server.IntegrationTests.Certificate.pfx", password: "Owin.Security.OpenIdConnect.Server"); options.AddEventHandler(builder => builder.UseInlineHandler(context => default)); options.AddEventHandler(builder => builder.UseInlineHandler(context => default)); options.AddEventHandler(builder => builder.UseInlineHandler(context => default)); options.AddEventHandler(builder => builder.UseInlineHandler(context => default)); options.AddEventHandler(builder => builder.UseInlineHandler(context => default)); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { var identity = new ClaimsIdentity("Bearer"); identity.AddClaim(Claims.Subject, "Bob le Magnifique"); context.Principal = new ClaimsPrincipal(identity); context.HandleAuthentication(); return default; }); builder.SetOrder(int.MaxValue); }); options.AddEventHandler(builder => { builder.UseInlineHandler(context => { var identity = new ClaimsIdentity("Bearer"); identity.AddClaim(Claims.Subject, "Bob le Magnifique"); context.Principal = new ClaimsPrincipal(identity); context.HandleAuthentication(); return default; }); builder.SetOrder(int.MaxValue); }); }); } protected abstract OpenIddictServerIntegrationTestClient CreateClient(Action configuration = null); protected OpenIddictApplicationManager CreateApplicationManager( Action>> configuration = null) { var manager = new Mock>( Mock.Of>(), Mock.Of(), Mock.Of>>(), Mock.Of>()); configuration?.Invoke(manager); return manager.Object; } protected OpenIddictAuthorizationManager CreateAuthorizationManager( Action>> configuration = null) { var manager = new Mock>( Mock.Of>(), Mock.Of(), Mock.Of>>(), Mock.Of>()); configuration?.Invoke(manager); return manager.Object; } protected OpenIddictScopeManager CreateScopeManager( Action>> configuration = null) { var manager = new Mock>( Mock.Of>(), Mock.Of(), Mock.Of>>(), Mock.Of>()); configuration?.Invoke(manager); return manager.Object; } protected OpenIddictTokenManager CreateTokenManager( Action>> configuration = null) { var manager = new Mock>( Mock.Of>(), Mock.Of(), Mock.Of>>(), Mock.Of>()); configuration?.Invoke(manager); return manager.Object; } public class OpenIddictApplication { } public class OpenIddictAuthorization { } public class OpenIddictScope { } public class OpenIddictToken { } } }