/* * 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.Text; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Client; using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Server; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Xunit; namespace OpenIddict.Server.Tests { public class OpenIddictServerInitializerTests { [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenRandomNumberGeneratorIsNull() { // Arrange var server = CreateAuthorizationServer(builder => { builder.Configure(options => options.RandomNumberGenerator = null); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/"); }); // Assert Assert.Equal("A random number generator must be registered.", exception.Message); } [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenApplicationProviderTypeAndInstanceAreProvided() { // Arrange var server = CreateAuthorizationServer(builder => { builder.Configure(options => { options.ApplicationProvider = new OpenIdConnectServerProvider(); options.ApplicationProviderType = typeof(OpenIdConnectServerProvider); }); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/"); }); // Assert Assert.Equal("An application provider cannot be registered when a type is specified.", exception.Message); } [Fact] public async Task PostConfigure_ThrowsAnExceptionForInvalidApplicationProviderType() { // Arrange var server = CreateAuthorizationServer(builder => { builder.Configure(options => options.ApplicationProviderType = typeof(object)); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/"); }); // Assert Assert.Equal("Application providers must inherit from OpenIdConnectServerProvider.", exception.Message); } [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenNoFlowIsEnabled() { // Arrange var server = CreateAuthorizationServer(); var client = new OpenIdConnectClient(server.CreateClient()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/"); }); // Assert Assert.Equal("At least one OAuth2/OpenID Connect flow must be enabled.", exception.Message); } [Theory] [InlineData(OpenIdConnectConstants.GrantTypes.AuthorizationCode)] [InlineData(OpenIdConnectConstants.GrantTypes.Implicit)] public async Task PostConfigure_ThrowsAnExceptionWhenAuthorizationEndpointIsDisabled(string flow) { // Arrange var server = CreateAuthorizationServer(builder => { builder.Configure(options => options.GrantTypes.Add(flow)) .Configure(options => options.AuthorizationEndpointPath = PathString.Empty); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/"); }); Assert.Equal("The authorization endpoint must be enabled to use the authorization code and implicit flows.", exception.Message); } [Theory] [InlineData(OpenIdConnectConstants.GrantTypes.AuthorizationCode)] [InlineData(OpenIdConnectConstants.GrantTypes.ClientCredentials)] [InlineData(OpenIdConnectConstants.GrantTypes.Password)] [InlineData(OpenIdConnectConstants.GrantTypes.RefreshToken)] public async Task PostConfigure_ThrowsAnExceptionWhenTokenEndpointIsDisabled(string flow) { // Arrange var server = CreateAuthorizationServer(builder => { builder.EnableAuthorizationEndpoint("/connect/authorize") .Configure(options => options.GrantTypes.Add(flow)) .Configure(options => options.TokenEndpointPath = PathString.Empty); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/"); }); Assert.Equal("The token endpoint must be enabled to use the authorization code, " + "client credentials, password and refresh token flows.", exception.Message); } [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenTokenStorageIsDisabled() { // Arrange var server = CreateAuthorizationServer(builder => { builder.EnableAuthorizationEndpoint("/connect/authorize") .EnableRevocationEndpoint("/connect/revocation") .AllowImplicitFlow() .DisableTokenStorage(); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/"); }); Assert.Equal("The revocation endpoint cannot be enabled when token storage is disabled.", exception.Message); } [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenUsingReferenceTokensWithTokenStorageDisabled() { // Arrange var server = CreateAuthorizationServer(builder => { builder.EnableAuthorizationEndpoint("/connect/authorize") .AllowImplicitFlow() .DisableTokenStorage() .UseReferenceTokens(); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/"); }); Assert.Equal("Reference tokens cannot be used when disabling token storage.", exception.Message); } [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenUsingSlidingExpirationWithoutRollingTokensAndWithTokenStorageDisabled() { // Arrange var server = CreateAuthorizationServer(builder => { builder.EnableAuthorizationEndpoint("/connect/authorize") .AllowImplicitFlow() .DisableTokenStorage(); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/"); }); Assert.Equal("Sliding expiration must be disabled when turning off " + "token storage if rolling tokens are not used.", exception.Message); } [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenUsingReferenceTokensIfAnAccessTokenHandlerIsSet() { // Arrange var server = CreateAuthorizationServer(builder => { builder.EnableAuthorizationEndpoint("/connect/authorize") .AllowImplicitFlow() .UseReferenceTokens() .UseJsonWebTokens(); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/"); }); Assert.Equal("Reference tokens cannot be used when configuring JWT as the access token format.", exception.Message); } [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenNoSigningKeyIsRegisteredIfAnAccessTokenHandlerIsSet() { // Arrange var server = CreateAuthorizationServer(builder => { builder.EnableAuthorizationEndpoint("/connect/authorize") .EnableTokenEndpoint("/connect/token") .AllowAuthorizationCodeFlow() .UseJsonWebTokens(); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/"); }); Assert.Equal(new StringBuilder() .AppendLine("At least one signing key must be registered when using JWT as the access token format.") .Append("Consider registering a certificate using 'services.AddOpenIddict().AddServer().AddSigningCertificate()' ") .Append("or 'services.AddOpenIddict().AddServer().AddDevelopmentSigningCertificate()' or call ") .Append("'services.AddOpenIddict().AddServer().AddEphemeralSigningKey()' to use an ephemeral key.") .ToString(), exception.Message); } [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenNoSigningKeyIsRegisteredIfTheImplicitFlowIsEnabled() { // Arrange var server = CreateAuthorizationServer(builder => { builder.EnableAuthorizationEndpoint("/connect/authorize") .AllowImplicitFlow(); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act and assert var exception = await Assert.ThrowsAsync(delegate { return client.GetAsync("/"); }); Assert.Equal(new StringBuilder() .AppendLine("At least one asymmetric signing key must be registered when enabling the implicit flow.") .Append("Consider registering a certificate using 'services.AddOpenIddict().AddServer().AddSigningCertificate()' ") .Append("or 'services.AddOpenIddict().AddServer().AddDevelopmentSigningCertificate()' or call ") .Append("'services.AddOpenIddict().AddServer().AddEphemeralSigningKey()' to use an ephemeral key.") .ToString(), exception.Message); } private static TestServer CreateAuthorizationServer(Action configuration = null) { var builder = new WebHostBuilder(); builder.UseEnvironment("Testing"); builder.ConfigureLogging(options => options.AddDebug()); builder.ConfigureServices(services => { services.AddAuthentication(); services.AddOptions(); services.AddDistributedMemoryCache(); services.AddOpenIddict() .AddCore(options => { options.SetDefaultApplicationEntity() .SetDefaultAuthorizationEntity() .SetDefaultScopeEntity() .SetDefaultTokenEntity(); }) .AddServer(options => configuration?.Invoke(options)); }); builder.Configure(app => { app.UseAuthentication(); app.Run(context => context.ChallengeAsync(OpenIddictServerDefaults.AuthenticationScheme)); }); return new TestServer(builder); } public class OpenIddictApplication { } public class OpenIddictAuthorization { } public class OpenIddictScope { } public class OpenIddictToken { } } }