Browse Source

Backport the refresh token generation fix to OpenIddict 1.x

pull/553/head
Kévin Chalet 9 years ago
parent
commit
13fe2c6e41
  1. 15
      README.md
  2. 64
      src/OpenIddict.Core/OpenIddictBuilder.cs
  3. 8
      src/OpenIddict/OpenIddictProvider.Signin.cs
  4. 16
      test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs
  5. 72
      test/OpenIddict.Tests/OpenIddictProviderTests.Signin.cs

15
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<ApplicationDbContext>(options =>
@ -218,13 +218,14 @@ using (var scope = app.ApplicationServices.GetRequiredService<IServiceScopeFacto
if (await manager.FindByClientIdAsync("[client identifier]", cancellationToken) == null)
{
var application = new OpenIddictApplication
var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = "[client identifier]",
RedirectUri = "[redirect uri]"
ClientSecret = "[client secret]",
RedirectUris = { new Uri("[redirect uri]") }
};
await manager.CreateAsync(application, "[client secret]", cancellationToken);
await manager.CreateAsync(descriptor, cancellationToken);
}
}
```

64
src/OpenIddict.Core/OpenIddictBuilder.cs

@ -66,7 +66,8 @@ namespace Microsoft.Extensions.DependencyInjection
public IServiceCollection Services { get; }
/// <summary>
/// Adds a custom application manager.
/// Adds a custom application manager derived from
/// <see cref="OpenIddictApplicationManager{TApplication}"/>.
/// </summary>
/// <typeparam name="TManager">The type of the custom manager.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -74,7 +75,8 @@ namespace Microsoft.Extensions.DependencyInjection
=> AddApplicationManager(typeof(TManager));
/// <summary>
/// Adds a custom application manager.
/// Adds a custom application manager derived from
/// <see cref="OpenIddictApplicationManager{TApplication}"/>.
/// </summary>
/// <param name="type">The type of the custom manager.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -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
}
/// <summary>
/// Adds a custom application store.
/// Adds a custom application store derived from
/// <see cref="IOpenIddictApplicationStore{TApplication}"/>.
/// </summary>
/// <typeparam name="TStore">The type of the custom store.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -105,7 +108,8 @@ namespace Microsoft.Extensions.DependencyInjection
=> AddApplicationStore(typeof(TStore));
/// <summary>
/// Adds a custom application store.
/// Adds a custom application store derived from
/// <see cref="IOpenIddictApplicationStore{TApplication}"/>.
/// </summary>
/// <param name="type">The type of the custom store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -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
}
/// <summary>
/// Adds a custom authorization manager.
/// Adds a custom authorization manager derived from
/// <see cref="OpenIddictAuthorizationManager{TAuthorization}"/>.
/// </summary>
/// <typeparam name="TManager">The type of the custom manager.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -136,7 +141,8 @@ namespace Microsoft.Extensions.DependencyInjection
=> AddAuthorizationManager(typeof(TManager));
/// <summary>
/// Adds a custom authorization manager.
/// Adds a custom authorization manager derived from
/// <see cref="OpenIddictAuthorizationManager{TAuthorization}"/>.
/// </summary>
/// <param name="type">The type of the custom manager.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -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
}
/// <summary>
/// Adds a custom authorization store.
/// Adds a custom authorization store derived from
/// <see cref="IOpenIddictAuthorizationStore{TAuthorization}"/>.
/// </summary>
/// <typeparam name="TStore">The type of the custom store.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -167,7 +174,8 @@ namespace Microsoft.Extensions.DependencyInjection
=> AddAuthorizationStore(typeof(TStore));
/// <summary>
/// Adds a custom authorization store.
/// Adds a custom authorization store derived from
/// <see cref="IOpenIddictAuthorizationStore{TAuthorization}"/>.
/// </summary>
/// <param name="type">The type of the custom store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -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
}
/// <summary>
/// Adds a custom scope manager.
/// Adds a custom scope manager derived from
/// <see cref="OpenIddictScopeManager{TScope}"/>.
/// </summary>
/// <typeparam name="TManager">The type of the custom manager.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -198,7 +207,8 @@ namespace Microsoft.Extensions.DependencyInjection
=> AddScopeManager(typeof(TManager));
/// <summary>
/// Adds a custom scope manager.
/// Adds a custom scope manager derived from
/// <see cref="OpenIddictScopeManager{TScope}"/>.
/// </summary>
/// <param name="type">The type of the custom manager.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -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
}
/// <summary>
/// Adds a custom scope store.
/// Adds a custom scope store derived from
/// <see cref="IOpenIddictScopeStore{TScope}"/>.
/// </summary>
/// <typeparam name="TStore">The type of the custom store.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -229,7 +240,8 @@ namespace Microsoft.Extensions.DependencyInjection
=> AddScopeStore(typeof(TStore));
/// <summary>
/// Adds a custom scope store.
/// Adds a custom scope store derived from
/// <see cref="IOpenIddictScopeStore{TScope}"/>.
/// </summary>
/// <param name="type">The type of the custom store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -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
}
/// <summary>
/// Adds a custom token manager.
/// Adds a custom token manager derived from
/// <see cref="OpenIddictTokenManager{TToken}"/>.
/// </summary>
/// <typeparam name="TManager">The type of the custom manager.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -260,7 +273,8 @@ namespace Microsoft.Extensions.DependencyInjection
=> AddTokenManager(typeof(TManager));
/// <summary>
/// Adds a custom token manager.
/// Adds a custom token manager derived from
/// <see cref="OpenIddictTokenManager{TToken}"/>.
/// </summary>
/// <param name="type">The type of the custom manager.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -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
}
/// <summary>
/// Adds a custom token store.
/// Adds a custom token store derived from
/// <see cref="IOpenIddictTokenStore{TToken}"/>.
/// </summary>
/// <typeparam name="TStore">The type of the custom store.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -291,7 +306,8 @@ namespace Microsoft.Extensions.DependencyInjection
=> AddTokenStore(typeof(TStore));
/// <summary>
/// Adds a custom token store.
/// Adds a custom token store derived from
/// <see cref="IOpenIddictTokenStore{TToken}"/>.
/// </summary>
/// <param name="type">The type of the custom store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
@ -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);

8
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.

16
test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs

@ -27,7 +27,7 @@ namespace OpenIddict.Core.Tests
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => builder.AddTokenStore(typeof(object)));
Assert.Equal("Custom stores must implement IOpenIddictTokenStore.", exception.Message);
Assert.Equal("The specified type is invalid.", exception.Message);
}
[Fact]

72
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<ISecureDataFormat<AuthenticationTicket>>();
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<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
});
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.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()
{

Loading…
Cancel
Save