|
|
|
@ -277,6 +277,78 @@ namespace OpenIddict.Tests |
|
|
|
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task ProcessSigninResponse_ReturnsErrorResponseWhenRedeemingAuthorizationCodeFails() |
|
|
|
{ |
|
|
|
// 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); |
|
|
|
|
|
|
|
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.IsValidAsync(token, It.IsAny<CancellationToken>())) |
|
|
|
.ReturnsAsync(true); |
|
|
|
|
|
|
|
instance.Setup(mock => mock.RedeemAsync(token, It.IsAny<CancellationToken>())) |
|
|
|
.ThrowsAsync(new Exception()); |
|
|
|
}); |
|
|
|
|
|
|
|
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.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.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error); |
|
|
|
Assert.Equal("The specified authorization code is no longer valid.", response.ErrorDescription); |
|
|
|
|
|
|
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()), Times.Exactly(2)); |
|
|
|
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task ProcessSigninResponse_RefreshTokenIsAutomaticallyRedeemedWhenRollingTokensAreEnabled() |
|
|
|
{ |
|
|
|
@ -295,6 +367,9 @@ namespace OpenIddict.Tests |
|
|
|
|
|
|
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
|
|
|
|
|
|
|
format.Setup(mock => mock.Protect(It.IsAny<AuthenticationTicket>())) |
|
|
|
.Returns("8xLOxBtZp8"); |
|
|
|
|
|
|
|
format.Setup(mock => mock.Unprotect("8xLOxBtZp8")) |
|
|
|
.Returns(ticket); |
|
|
|
|
|
|
|
@ -331,6 +406,75 @@ namespace OpenIddict.Tests |
|
|
|
}); |
|
|
|
|
|
|
|
// Assert
|
|
|
|
Assert.NotNull(response.RefreshToken); |
|
|
|
|
|
|
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()), Times.Exactly(2)); |
|
|
|
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task ProcessSigninResponse_ReturnsErrorResponseWhenRedeemingRefreshTokenFails() |
|
|
|
{ |
|
|
|
// 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.SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103"); |
|
|
|
ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken); |
|
|
|
ticket.SetScopes(OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.OfflineAccess); |
|
|
|
|
|
|
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
|
|
|
|
|
|
|
format.Setup(mock => mock.Protect(It.IsAny<AuthenticationTicket>())) |
|
|
|
.Returns("8xLOxBtZp8"); |
|
|
|
|
|
|
|
format.Setup(mock => mock.Unprotect("8xLOxBtZp8")) |
|
|
|
.Returns(ticket); |
|
|
|
|
|
|
|
var token = new OpenIddictToken(); |
|
|
|
|
|
|
|
var manager = CreateTokenManager(instance => |
|
|
|
{ |
|
|
|
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", 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); |
|
|
|
|
|
|
|
instance.Setup(mock => mock.RedeemAsync(token, It.IsAny<CancellationToken>())) |
|
|
|
.ThrowsAsync(new Exception()); |
|
|
|
}); |
|
|
|
|
|
|
|
var server = CreateAuthorizationServer(builder => |
|
|
|
{ |
|
|
|
builder.Services.AddSingleton(manager); |
|
|
|
|
|
|
|
builder.UseRollingTokens(); |
|
|
|
|
|
|
|
builder.Configure(options => options.RefreshTokenFormat = format.Object); |
|
|
|
}); |
|
|
|
|
|
|
|
var client = new OpenIdConnectClient(server.CreateClient()); |
|
|
|
|
|
|
|
// Act
|
|
|
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest |
|
|
|
{ |
|
|
|
GrantType = OpenIdConnectConstants.GrantTypes.RefreshToken, |
|
|
|
RefreshToken = "8xLOxBtZp8" |
|
|
|
}); |
|
|
|
|
|
|
|
// Assert
|
|
|
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error); |
|
|
|
Assert.Equal("The specified authorization code is no longer valid.", response.ErrorDescription); |
|
|
|
|
|
|
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()), Times.Exactly(2)); |
|
|
|
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
} |
|
|
|
@ -387,6 +531,8 @@ namespace OpenIddict.Tests |
|
|
|
}); |
|
|
|
|
|
|
|
// Assert
|
|
|
|
Assert.Null(response.RefreshToken); |
|
|
|
|
|
|
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()), Times.Exactly(2)); |
|
|
|
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny<CancellationToken>()), Times.Never()); |
|
|
|
} |
|
|
|
@ -410,6 +556,9 @@ namespace OpenIddict.Tests |
|
|
|
|
|
|
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
|
|
|
|
|
|
|
format.Setup(mock => mock.Protect(It.IsAny<AuthenticationTicket>())) |
|
|
|
.Returns("8xLOxBtZp8"); |
|
|
|
|
|
|
|
format.Setup(mock => mock.Unprotect("8xLOxBtZp8")) |
|
|
|
.Returns(ticket); |
|
|
|
|
|
|
|
@ -452,6 +601,8 @@ namespace OpenIddict.Tests |
|
|
|
}); |
|
|
|
|
|
|
|
// Assert
|
|
|
|
Assert.NotNull(response.RefreshToken); |
|
|
|
|
|
|
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()), Times.Exactly(2)); |
|
|
|
Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[1], It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[2], It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
@ -516,6 +667,8 @@ namespace OpenIddict.Tests |
|
|
|
}); |
|
|
|
|
|
|
|
// Assert
|
|
|
|
Assert.Null(response.RefreshToken); |
|
|
|
|
|
|
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()), Times.Exactly(2)); |
|
|
|
Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[1], It.IsAny<CancellationToken>()), Times.Never()); |
|
|
|
Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[2], It.IsAny<CancellationToken>()), Times.Never()); |
|
|
|
@ -653,6 +806,75 @@ namespace OpenIddict.Tests |
|
|
|
It.IsAny<CancellationToken>()), Times.Never()); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task ProcessSigninResponse_ReturnsErrorResponseWhenExtendingLifetimeOfExistingTokenFailed() |
|
|
|
{ |
|
|
|
// Arrange
|
|
|
|
var ticket = new AuthenticationTicket( |
|
|
|
new ClaimsPrincipal(), |
|
|
|
new AuthenticationProperties(), |
|
|
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
|
|
|
|
|
|
|
ticket.SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103"); |
|
|
|
ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken); |
|
|
|
ticket.SetScopes(OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.OfflineAccess); |
|
|
|
|
|
|
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
|
|
|
|
|
|
|
format.Setup(mock => mock.Protect(It.IsAny<AuthenticationTicket>())) |
|
|
|
.Returns("8xLOxBtZp8"); |
|
|
|
|
|
|
|
format.Setup(mock => mock.Unprotect("8xLOxBtZp8")) |
|
|
|
.Returns(ticket); |
|
|
|
|
|
|
|
var token = new OpenIddictToken(); |
|
|
|
|
|
|
|
var manager = CreateTokenManager(instance => |
|
|
|
{ |
|
|
|
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", 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); |
|
|
|
|
|
|
|
instance.Setup(mock => mock.ExtendAsync(token, It.IsAny<DateTimeOffset?>(), It.IsAny<CancellationToken>())) |
|
|
|
.ThrowsAsync(new Exception()); |
|
|
|
}); |
|
|
|
|
|
|
|
var server = CreateAuthorizationServer(builder => |
|
|
|
{ |
|
|
|
builder.Services.AddSingleton(manager); |
|
|
|
|
|
|
|
builder.Configure(options => |
|
|
|
{ |
|
|
|
options.SystemClock = Mock.Of<ISystemClock>(mock => mock.UtcNow == |
|
|
|
new DateTimeOffset(2017, 01, 05, 00, 00, 00, TimeSpan.Zero)); |
|
|
|
options.RefreshTokenLifetime = TimeSpan.FromDays(10); |
|
|
|
options.RefreshTokenFormat = format.Object; |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
var client = new OpenIdConnectClient(server.CreateClient()); |
|
|
|
|
|
|
|
// Act
|
|
|
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest |
|
|
|
{ |
|
|
|
GrantType = OpenIdConnectConstants.GrantTypes.RefreshToken, |
|
|
|
RefreshToken = "8xLOxBtZp8" |
|
|
|
}); |
|
|
|
|
|
|
|
// Assert
|
|
|
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error); |
|
|
|
Assert.Equal("The specified refresh token is no longer valid.", response.ErrorDescription); |
|
|
|
|
|
|
|
Mock.Get(manager).Verify(mock => mock.ExtendAsync(token, |
|
|
|
new DateTimeOffset(2017, 01, 15, 00, 00, 00, TimeSpan.Zero), |
|
|
|
It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public async Task ProcessSigninResponse_AdHocAuthorizationIsAutomaticallyCreated() |
|
|
|
{ |
|
|
|
|