diff --git a/README.md b/README.md index d0db6382..04417933 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ OpenIddict is based on OpenIddict fully supports the **[code/implicit/hybrid flows](http://openid.net/specs/openid-connect-core-1_0.html)** and the **[client credentials/resource owner password grants](https://tools.ietf.org/html/rfc6749)**. You can also create your own custom grant types. -Note: OpenIddict uses **[Entity Framework Core](https://github.com/aspnet/EntityFramework)** by default, but you can also provide your own store. +Note: OpenIddict natively supports **[Entity Framework Core](https://github.com/aspnet/EntityFramework)** and **[Entity Framework 6](https://github.com/aspnet/EntityFramework6)** out-of-the-box, but you can also provide your own stores. > Note: **the OpenIddict 2.x packages are only compatible with ASP.NET Core 2.x**. > If your application targets ASP.NET Core 1.x, use the OpenIddict 1.x packages. @@ -124,7 +124,7 @@ public void ConfigureServices(IServiceCollection services) [Configuration and options](https://github.com/openiddict/core/wiki/Configuration-and-options) in the project wiki. - - **Make sure the authentication middleware is registered before all the other middleware, including `app.UseMvc()`: + - **Make sure the authentication middleware is registered before all the other middleware, including `app.UseMvc()`**: ```csharp public void Configure(IApplicationBuilder app) @@ -135,7 +135,7 @@ public void Configure(IApplicationBuilder app) } ``` - - **Update your Entity Framework context registration to register the OpenIddict entities**: + - **Update your Entity Framework Core context registration to register the OpenIddict entities**: ```csharp services.AddDbContext(options => @@ -218,13 +218,14 @@ using (var scope = app.ApplicationServices.GetRequiredService - /// Adds a custom application manager. + /// Adds a custom application manager derived from + /// . /// /// The type of the custom manager. /// The . @@ -74,7 +75,8 @@ namespace Microsoft.Extensions.DependencyInjection => AddApplicationManager(typeof(TManager)); /// - /// Adds a custom application manager. + /// Adds a custom application manager derived from + /// . /// /// The type of the custom manager. /// The . @@ -88,7 +90,7 @@ namespace Microsoft.Extensions.DependencyInjection var contract = typeof(OpenIddictApplicationManager<>).MakeGenericType(ApplicationType); if (!contract.IsAssignableFrom(type)) { - throw new InvalidOperationException("Custom managers must be derived from OpenIddictApplicationManager."); + throw new InvalidOperationException("The specified type is invalid."); } Services.AddScoped(contract, type); @@ -97,7 +99,8 @@ namespace Microsoft.Extensions.DependencyInjection } /// - /// Adds a custom application store. + /// Adds a custom application store derived from + /// . /// /// The type of the custom store. /// The . @@ -105,7 +108,8 @@ namespace Microsoft.Extensions.DependencyInjection => AddApplicationStore(typeof(TStore)); /// - /// Adds a custom application store. + /// Adds a custom application store derived from + /// . /// /// The type of the custom store. /// The . @@ -119,7 +123,7 @@ namespace Microsoft.Extensions.DependencyInjection var contract = typeof(IOpenIddictApplicationStore<>).MakeGenericType(ApplicationType); if (!contract.IsAssignableFrom(type)) { - throw new InvalidOperationException("Custom stores must implement IOpenIddictApplicationStore."); + throw new InvalidOperationException("The specified type is invalid."); } Services.AddScoped(contract, type); @@ -128,7 +132,8 @@ namespace Microsoft.Extensions.DependencyInjection } /// - /// Adds a custom authorization manager. + /// Adds a custom authorization manager derived from + /// . /// /// The type of the custom manager. /// The . @@ -136,7 +141,8 @@ namespace Microsoft.Extensions.DependencyInjection => AddAuthorizationManager(typeof(TManager)); /// - /// Adds a custom authorization manager. + /// Adds a custom authorization manager derived from + /// . /// /// The type of the custom manager. /// The . @@ -150,7 +156,7 @@ namespace Microsoft.Extensions.DependencyInjection var contract = typeof(OpenIddictAuthorizationManager<>).MakeGenericType(AuthorizationType); if (!contract.IsAssignableFrom(type)) { - throw new InvalidOperationException("Custom managers must be derived from OpenIddictAuthorizationManager."); + throw new InvalidOperationException("The specified type is invalid."); } Services.AddScoped(contract, type); @@ -159,7 +165,8 @@ namespace Microsoft.Extensions.DependencyInjection } /// - /// Adds a custom authorization store. + /// Adds a custom authorization store derived from + /// . /// /// The type of the custom store. /// The . @@ -167,7 +174,8 @@ namespace Microsoft.Extensions.DependencyInjection => AddAuthorizationStore(typeof(TStore)); /// - /// Adds a custom authorization store. + /// Adds a custom authorization store derived from + /// . /// /// The type of the custom store. /// The . @@ -181,7 +189,7 @@ namespace Microsoft.Extensions.DependencyInjection var contract = typeof(IOpenIddictAuthorizationStore<>).MakeGenericType(AuthorizationType); if (!contract.IsAssignableFrom(type)) { - throw new InvalidOperationException("Custom stores must implement IOpenIddictAuthorizationStore."); + throw new InvalidOperationException("The specified type is invalid."); } Services.AddScoped(contract, type); @@ -190,7 +198,8 @@ namespace Microsoft.Extensions.DependencyInjection } /// - /// Adds a custom scope manager. + /// Adds a custom scope manager derived from + /// . /// /// The type of the custom manager. /// The . @@ -198,7 +207,8 @@ namespace Microsoft.Extensions.DependencyInjection => AddScopeManager(typeof(TManager)); /// - /// Adds a custom scope manager. + /// Adds a custom scope manager derived from + /// . /// /// The type of the custom manager. /// The . @@ -212,7 +222,7 @@ namespace Microsoft.Extensions.DependencyInjection var contract = typeof(OpenIddictScopeManager<>).MakeGenericType(ScopeType); if (!contract.IsAssignableFrom(type)) { - throw new InvalidOperationException("Custom managers must be derived from OpenIddictScopeManager."); + throw new InvalidOperationException("The specified type is invalid."); } Services.AddScoped(contract, type); @@ -221,7 +231,8 @@ namespace Microsoft.Extensions.DependencyInjection } /// - /// Adds a custom scope store. + /// Adds a custom scope store derived from + /// . /// /// The type of the custom store. /// The . @@ -229,7 +240,8 @@ namespace Microsoft.Extensions.DependencyInjection => AddScopeStore(typeof(TStore)); /// - /// Adds a custom scope store. + /// Adds a custom scope store derived from + /// . /// /// The type of the custom store. /// The . @@ -243,7 +255,7 @@ namespace Microsoft.Extensions.DependencyInjection var contract = typeof(IOpenIddictScopeStore<>).MakeGenericType(ScopeType); if (!contract.IsAssignableFrom(type)) { - throw new InvalidOperationException("Custom stores must implement IOpenIddictScopeStore."); + throw new InvalidOperationException("The specified type is invalid."); } Services.AddScoped(contract, type); @@ -252,7 +264,8 @@ namespace Microsoft.Extensions.DependencyInjection } /// - /// Adds a custom token manager. + /// Adds a custom token manager derived from + /// . /// /// The type of the custom manager. /// The . @@ -260,7 +273,8 @@ namespace Microsoft.Extensions.DependencyInjection => AddTokenManager(typeof(TManager)); /// - /// Adds a custom token manager. + /// Adds a custom token manager derived from + /// . /// /// The type of the custom manager. /// The . @@ -274,7 +288,7 @@ namespace Microsoft.Extensions.DependencyInjection var contract = typeof(OpenIddictTokenManager<>).MakeGenericType(TokenType); if (!contract.IsAssignableFrom(type)) { - throw new InvalidOperationException("Custom managers must be derived from OpenIddictTokenManager."); + throw new InvalidOperationException("The specified type is invalid."); } Services.AddScoped(contract, type); @@ -283,7 +297,8 @@ namespace Microsoft.Extensions.DependencyInjection } /// - /// Adds a custom token store. + /// Adds a custom token store derived from + /// . /// /// The type of the custom store. /// The . @@ -291,7 +306,8 @@ namespace Microsoft.Extensions.DependencyInjection => AddTokenStore(typeof(TStore)); /// - /// Adds a custom token store. + /// Adds a custom token store derived from + /// . /// /// The type of the custom store. /// The . @@ -305,7 +321,7 @@ namespace Microsoft.Extensions.DependencyInjection var contract = typeof(IOpenIddictTokenStore<>).MakeGenericType(TokenType); if (!contract.IsAssignableFrom(type)) { - throw new InvalidOperationException("Custom stores must implement IOpenIddictTokenStore."); + throw new InvalidOperationException("The specified type is invalid."); } Services.AddScoped(contract, type); diff --git a/src/OpenIddict/OpenIddictProvider.Signin.cs b/src/OpenIddict/OpenIddictProvider.Signin.cs index 3f8dc9d2..72c38136 100644 --- a/src/OpenIddict/OpenIddictProvider.Signin.cs +++ b/src/OpenIddict/OpenIddictProvider.Signin.cs @@ -64,10 +64,14 @@ namespace OpenIddict context.IncludeIdentityToken = context.Ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId); } + context.IncludeRefreshToken = context.Ticket.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess); + // Always include a refresh token for grant_type=refresh_token requests if // rolling tokens are enabled and if the offline_access scope was specified. - context.IncludeRefreshToken = context.Request.IsRefreshTokenGrantType() && options.UseRollingTokens && - context.Ticket.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess); + if (context.Request.IsRefreshTokenGrantType()) + { + context.IncludeRefreshToken &= options.UseRollingTokens; + } // If token revocation was explicitly disabled, // none of the following security routines apply. diff --git a/test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs b/test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs index 6ef5830b..c547ad8e 100644 --- a/test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs +++ b/test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs @@ -27,7 +27,7 @@ namespace OpenIddict.Core.Tests // Act and assert var exception = Assert.Throws(() => builder.AddApplicationManager(typeof(object))); - Assert.Equal("Custom managers must be derived from OpenIddictApplicationManager.", exception.Message); + Assert.Equal("The specified type is invalid.", exception.Message); } [Fact] @@ -67,7 +67,7 @@ namespace OpenIddict.Core.Tests // Act and assert var exception = Assert.Throws(() => builder.AddApplicationStore(typeof(object))); - Assert.Equal("Custom stores must implement IOpenIddictApplicationStore.", exception.Message); + Assert.Equal("The specified type is invalid.", exception.Message); } [Fact] @@ -105,7 +105,7 @@ namespace OpenIddict.Core.Tests // Act and assert var exception = Assert.Throws(() => builder.AddAuthorizationManager(typeof(object))); - Assert.Equal("Custom managers must be derived from OpenIddictAuthorizationManager.", exception.Message); + Assert.Equal("The specified type is invalid.", exception.Message); } [Fact] @@ -145,7 +145,7 @@ namespace OpenIddict.Core.Tests // Act and assert var exception = Assert.Throws(() => builder.AddAuthorizationStore(typeof(object))); - Assert.Equal("Custom stores must implement IOpenIddictAuthorizationStore.", exception.Message); + Assert.Equal("The specified type is invalid.", exception.Message); } [Fact] @@ -183,7 +183,7 @@ namespace OpenIddict.Core.Tests // Act and assert var exception = Assert.Throws(() => builder.AddScopeManager(typeof(object))); - Assert.Equal("Custom managers must be derived from OpenIddictScopeManager.", exception.Message); + Assert.Equal("The specified type is invalid.", exception.Message); } [Fact] @@ -223,7 +223,7 @@ namespace OpenIddict.Core.Tests // Act and assert var exception = Assert.Throws(() => builder.AddScopeStore(typeof(object))); - Assert.Equal("Custom stores must implement IOpenIddictScopeStore.", exception.Message); + Assert.Equal("The specified type is invalid.", exception.Message); } [Fact] @@ -261,7 +261,7 @@ namespace OpenIddict.Core.Tests // Act and assert var exception = Assert.Throws(() => builder.AddTokenManager(typeof(object))); - Assert.Equal("Custom managers must be derived from OpenIddictTokenManager.", exception.Message); + Assert.Equal("The specified type is invalid.", exception.Message); } [Fact] @@ -301,7 +301,7 @@ namespace OpenIddict.Core.Tests // Act and assert var exception = Assert.Throws(() => builder.AddTokenStore(typeof(object))); - Assert.Equal("Custom stores must implement IOpenIddictTokenStore.", exception.Message); + Assert.Equal("The specified type is invalid.", exception.Message); } [Fact] diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Signin.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Signin.cs index b4962864..7ec4f881 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Signin.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Signin.cs @@ -93,6 +93,78 @@ namespace OpenIddict.Tests value.Properties.Items["custom_property_in_original_ticket"] == "original_value" && value.Properties.Items["custom_property_in_new_ticket"] == "new_value"))); } + + [Fact] + public async Task ProcessSigninResponse_RefreshTokenIsIssuedForAuthorizationCodeRequestsWhenRollingTokensAreEnabled() + { + // Arrange + var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); + identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "Bob le Bricoleur"); + + var ticket = new AuthenticationTicket( + new ClaimsPrincipal(identity), + new AuthenticationProperties(), + OpenIdConnectServerDefaults.AuthenticationScheme); + + ticket.SetPresenters("Fabrikam"); + ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56"); + ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.AuthorizationCode); + ticket.SetScopes(OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.OfflineAccess); + + var format = new Mock>(); + + format.Setup(mock => mock.Unprotect("SplxlOBeZQQYbYS6WxSbIA")) + .Returns(ticket); + + var token = new OpenIddictToken(); + + var manager = CreateTokenManager(instance => + { + instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) + .ReturnsAsync(token); + + instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny())) + .ReturnsAsync(false); + + instance.Setup(mock => mock.IsValidAsync(token, It.IsAny())) + .ReturnsAsync(true); + }); + + var server = CreateAuthorizationServer(builder => + { + builder.Services.AddSingleton(CreateApplicationManager(instance => + { + var application = new OpenIddictApplication(); + + instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny())) + .ReturnsAsync(application); + + instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny())) + .ReturnsAsync(OpenIddictConstants.ClientTypes.Public); + })); + + builder.Services.AddSingleton(manager); + + builder.UseRollingTokens(); + + builder.Configure(options => options.AuthorizationCodeFormat = format.Object); + }); + + var client = new OpenIdConnectClient(server.CreateClient()); + + // Act + var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest + { + ClientId = "Fabrikam", + Code = "SplxlOBeZQQYbYS6WxSbIA", + GrantType = OpenIdConnectConstants.GrantTypes.AuthorizationCode, + RedirectUri = "http://www.fabrikam.com/path" + }); + + // Assert + Assert.NotNull(response.RefreshToken); + } + [Fact] public async Task ProcessSigninResponse_RefreshTokenIsAlwaysIssuedWhenRollingTokensAreEnabled() {