20 changed files with 2328 additions and 17 deletions
Binary file not shown.
@ -0,0 +1,563 @@ |
|||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using AspNet.Security.OpenIdConnect.Extensions; |
||||
|
using Microsoft.Extensions.Caching.Distributed; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Moq; |
||||
|
using Newtonsoft.Json; |
||||
|
using Newtonsoft.Json.Bson; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace OpenIddict.Core.Tests.Infrastructure { |
||||
|
public partial class OpenIddictProviderTests { |
||||
|
[Fact] |
||||
|
public async Task ExtractAuthorizationRequest_UnsupportedRequestParameterIsRejected() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
Request = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJodHRwOi8vd3d3LmZhYnJpa2FtLmNvbSIsImF1ZCI6Imh" + |
||||
|
"0dHA6Ly93d3cuY29udG9zby5jb20iLCJyZXNwb25zZV90eXBlIjoiY29kZSIsImNsaWVudF9pZC" + |
||||
|
"I6IkZhYnJpa2FtIiwicmVkaXJlY3RfdXJpIjoiaHR0cDovL3d3dy5mYWJyaWthbS5jb20vcGF0aCJ9.", |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Code, |
||||
|
Scope = OpenIdConnectConstants.Scopes.OpenId |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.RequestNotSupported, response.Error); |
||||
|
Assert.Equal("The request parameter is not supported.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ExtractAuthorizationRequest_UnsupportedRequestUriParameterIsRejected() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
RequestUri = "http://www.fabrikam.com/request/GkurKxf5T0Y-mnPFCHqWOMiZi4VS138cQO_V7PZHAdM", |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Code, |
||||
|
Scope = OpenIdConnectConstants.Scopes.OpenId |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.RequestUriNotSupported, response.Error); |
||||
|
Assert.Equal("The request_uri parameter is not supported.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ExtractAuthorizationRequest_InvalidRequestIdParameterIsRejected() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
RequestId = "EFAF3596-F868-497F-96BB-AA2AD1F8B7E7", |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Code |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("Invalid request: timeout expired.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateAuthorizationRequest_UnknownResponseTypeParameterIsRejected() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = "unknown_response_type" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.UnsupportedResponseType, response.Error); |
||||
|
Assert.Equal("The specified response_type parameter is not supported.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.AuthorizationCode, "code")] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.AuthorizationCode, "code id_token")] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.AuthorizationCode, "code id_token token")] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.AuthorizationCode, "code token")] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit, "code id_token")] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit, "code id_token token")] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit, "code token")] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit, "id_token")] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit, "id_token token")] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit, "token")] |
||||
|
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenCorrespondingFlowIsDisabled(string flow, string type) { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Configure(options => options.GrantTypes.Remove(flow)); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
Nonce = "n-0S6_WzA2Mj", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = type, |
||||
|
Scope = OpenIdConnectConstants.Scopes.OpenId |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.UnsupportedResponseType, response.Error); |
||||
|
Assert.Equal("The specified response_type parameter is not allowed.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateAuthorizationRequest_RequestWithOfflineAccessScopeIsRejectedWhenRefreshTokenFlowIsDisabled() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Configure(options => options.GrantTypes.Remove(OpenIdConnectConstants.GrantTypes.RefreshToken)); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Code, |
||||
|
Scope = OpenIdConnectConstants.Scopes.OfflineAccess |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("The 'offline_access' scope is not allowed.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateAuthorizationRequest_UnknownResponseModeParameterIsRejected() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseMode = "unknown_response_mode", |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Code |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("The specified response_mode parameter is not supported.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenRedirectUriIsMissing() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
RedirectUri = null, |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Code |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("The required redirect_uri parameter was missing.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenCodeChallengeMethodIsMissing() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
CodeChallenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", |
||||
|
CodeChallengeMethod = null, |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Code |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("The 'code_challenge_method' parameter must be specified.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenCodeChallengeMethodIsPlain() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
CodeChallenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", |
||||
|
CodeChallengeMethod = OpenIdConnectConstants.CodeChallengeMethods.Plain, |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Code |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("The specified response_type parameter is not allowed when using PKCE.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData("code id_token token")] |
||||
|
[InlineData("code token")] |
||||
|
public async Task ValidateAuthorizationRequest_CodeChallengeRequestWithForbiddenResponseTypeIsRejected(string type) { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
CodeChallenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", |
||||
|
CodeChallengeMethod = OpenIdConnectConstants.CodeChallengeMethods.Sha256, |
||||
|
Nonce = "n-0S6_WzA2Mj", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = type, |
||||
|
Scope = OpenIdConnectConstants.Scopes.OpenId |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("The specified response_type parameter is not allowed when using PKCE.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenClientCannotBeFound() { |
||||
|
// Arrange
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(null); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Code |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); |
||||
|
Assert.Equal("Application not found in the database: ensure that your client_id is correct.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenRedirectUriIsInvalid() { |
||||
|
// Arrange
|
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path")) |
||||
|
.ReturnsAsync(false); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Code |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); |
||||
|
Assert.Equal("Invalid redirect_uri.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData("code id_token token")] |
||||
|
[InlineData("code token")] |
||||
|
[InlineData("id_token token")] |
||||
|
[InlineData("token")] |
||||
|
public async Task ValidateAuthorizationRequest_ImplicitOrHybridRequestIsRejectedWhenClientIsConfidential(string type) { |
||||
|
// Arrange
|
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path")) |
||||
|
.ReturnsAsync(true); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
Nonce = "n-0S6_WzA2Mj", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = type, |
||||
|
Scope = OpenIdConnectConstants.Scopes.OpenId |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("Confidential clients are not allowed to retrieve " + |
||||
|
"an access token from the authorization endpoint.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.GetClientTypeAsync(application), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleAuthorizationRequest_RequestIsPersistedInDistributedCache() { |
||||
|
// Arrange
|
||||
|
var cache = new Mock<IDistributedCache>(); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path")) |
||||
|
.ReturnsAsync(true); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public); |
||||
|
})); |
||||
|
|
||||
|
builder.Services.AddSingleton(cache.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Token |
||||
|
}); |
||||
|
|
||||
|
var identifier = (string) response[OpenIdConnectConstants.Parameters.RequestId]; |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(1, response.Count()); |
||||
|
Assert.NotNull(identifier); |
||||
|
|
||||
|
cache.Verify(mock => mock.SetAsync( |
||||
|
OpenIddictConstants.Environment.AuthorizationRequest + identifier, |
||||
|
It.IsAny<byte[]>(), |
||||
|
It.IsAny<DistributedCacheEntryOptions>()), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData("code")] |
||||
|
[InlineData("code id_token")] |
||||
|
[InlineData("code id_token token")] |
||||
|
[InlineData("code token")] |
||||
|
[InlineData("id_token")] |
||||
|
[InlineData("id_token token")] |
||||
|
[InlineData("token")] |
||||
|
public async Task HandleAuthorizationRequest_RequestsAreNotHandledLocally(string type) { |
||||
|
// Arrange
|
||||
|
var request = new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
Nonce = "n-0S6_WzA2Mj", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = type, |
||||
|
Scope = OpenIdConnectConstants.Scopes.OpenId |
||||
|
}; |
||||
|
|
||||
|
var stream = new MemoryStream(); |
||||
|
using (var writer = new BsonWriter(stream)) { |
||||
|
writer.CloseOutput = false; |
||||
|
|
||||
|
var serializer = JsonSerializer.CreateDefault(); |
||||
|
serializer.Serialize(writer, request); |
||||
|
} |
||||
|
|
||||
|
var cache = new Mock<IDistributedCache>(); |
||||
|
|
||||
|
cache.Setup(mock => mock.GetAsync(OpenIddictConstants.Environment.AuthorizationRequest + |
||||
|
"b2ee7815-5579-4ff7-86b0-ba671b939d96")) |
||||
|
.ReturnsAsync(stream.ToArray()); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path")) |
||||
|
.ReturnsAsync(true); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public); |
||||
|
})); |
||||
|
|
||||
|
builder.Services.AddSingleton(CreateTokenManager(instance => { |
||||
|
instance.Setup(mock => mock.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode)) |
||||
|
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); |
||||
|
})); |
||||
|
|
||||
|
builder.Services.AddSingleton(cache.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
RequestId = "b2ee7815-5579-4ff7-86b0-ba671b939d96" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.True(!string.IsNullOrEmpty(response.AccessToken) || |
||||
|
!string.IsNullOrEmpty(response.Code) || |
||||
|
!string.IsNullOrEmpty(response.IdToken)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ApplyAuthorizationResponse_RequestIsRemovedFromDistributedCache() { |
||||
|
// Arrange
|
||||
|
var request = new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Token |
||||
|
}; |
||||
|
|
||||
|
var stream = new MemoryStream(); |
||||
|
using (var writer = new BsonWriter(stream)) { |
||||
|
writer.CloseOutput = false; |
||||
|
|
||||
|
var serializer = JsonSerializer.CreateDefault(); |
||||
|
serializer.Serialize(writer, request); |
||||
|
} |
||||
|
|
||||
|
var cache = new Mock<IDistributedCache>(); |
||||
|
|
||||
|
cache.Setup(mock => mock.GetAsync(OpenIddictConstants.Environment.AuthorizationRequest + |
||||
|
"b2ee7815-5579-4ff7-86b0-ba671b939d96")) |
||||
|
.ReturnsAsync(stream.ToArray()); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path")) |
||||
|
.ReturnsAsync(true); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public); |
||||
|
})); |
||||
|
|
||||
|
builder.Services.AddSingleton(cache.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
RequestId = "b2ee7815-5579-4ff7-86b0-ba671b939d96" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.NotNull(response.AccessToken); |
||||
|
|
||||
|
cache.Verify(mock => mock.RemoveAsync( |
||||
|
OpenIddictConstants.Environment.AuthorizationRequest + |
||||
|
"b2ee7815-5579-4ff7-86b0-ba671b939d96"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ApplyAuthorizationResponse_ErroredRequestIsNotHandledLocallyWhenStatusCodeMiddlewareIsEnabled() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path")) |
||||
|
.ReturnsAsync(true); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public); |
||||
|
})); |
||||
|
|
||||
|
builder.EnableAuthorizationEndpoint("/authorize-status-code-middleware"); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync("/authorize-status-code-middleware", new OpenIdConnectRequest { |
||||
|
ClientId = null, |
||||
|
RedirectUri = null, |
||||
|
ResponseType = null |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, (string) response["error_custom"]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,104 @@ |
|||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using AspNet.Security.OpenIdConnect.Extensions; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace OpenIddict.Core.Tests.Infrastructure { |
||||
|
public partial class OpenIddictProviderTests { |
||||
|
[Fact] |
||||
|
public async Task HandleConfigurationRequest_PlainCodeChallengeMethodIsNotReturned() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.GetAsync(ConfigurationEndpoint); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.DoesNotContain( |
||||
|
OpenIdConnectConstants.CodeChallengeMethods.Plain, |
||||
|
response[OpenIdConnectConstants.Metadata.CodeChallengeMethodsSupported].Values<string>()); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.AuthorizationCode)] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.ClientCredentials)] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit)] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.Password)] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.RefreshToken)] |
||||
|
public async Task HandleConfigurationRequest_EnabledFlowsAreReturned(string flow) { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Configure(options => { |
||||
|
options.GrantTypes.Clear(); |
||||
|
options.GrantTypes.Add(flow); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.GetAsync(ConfigurationEndpoint); |
||||
|
var types = response[OpenIdConnectConstants.Metadata.GrantTypesSupported].Values<string>(); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(1, types.Count()); |
||||
|
Assert.Contains(flow, types); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(OpenIdConnectConstants.Scopes.Profile)] |
||||
|
[InlineData(OpenIdConnectConstants.Scopes.Email)] |
||||
|
[InlineData(OpenIdConnectConstants.Scopes.Phone)] |
||||
|
[InlineData(OpenIddictConstants.Scopes.Roles)] |
||||
|
public async Task HandleConfigurationRequest_StandardScopesAreExposed(string scope) { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.GetAsync(ConfigurationEndpoint); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Contains(scope, response[OpenIdConnectConstants.Metadata.ScopesSupported].Values<string>()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleConfigurationRequest_OfflineAccessScopeIsReturnedWhenRefreshTokenFlowIsEnabled() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.GetAsync(ConfigurationEndpoint); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Contains(OpenIdConnectConstants.Scopes.OfflineAccess, |
||||
|
response[OpenIdConnectConstants.Metadata.ScopesSupported].Values<string>()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleConfigurationRequest_OfflineAccessScopeIsReturnedWhenRefreshTokenFlowIsDisabled() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Configure(options => { |
||||
|
// Note: at least one flow must be enabled.
|
||||
|
options.GrantTypes.Clear(); |
||||
|
options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.AuthorizationCode); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.GetAsync(ConfigurationEndpoint); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.DoesNotContain(OpenIdConnectConstants.Scopes.OfflineAccess, |
||||
|
response[OpenIdConnectConstants.Metadata.ScopesSupported].Values<string>()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,552 @@ |
|||||
|
using System.Security.Claims; |
||||
|
using System.Threading.Tasks; |
||||
|
using AspNet.Security.OpenIdConnect.Extensions; |
||||
|
using AspNet.Security.OpenIdConnect.Server; |
||||
|
using Microsoft.AspNetCore.Authentication; |
||||
|
using Microsoft.AspNetCore.Http.Authentication; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Moq; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace OpenIddict.Core.Tests.Infrastructure { |
||||
|
public partial class OpenIddictProviderTests { |
||||
|
[Theory] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.AuthorizationCode)] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.ClientCredentials)] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.Password)] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.RefreshToken)] |
||||
|
public async Task ValidateTokenRequest_RequestIsRejectedWhenFlowIsNotEnabled(string flow) { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Configure(options => options.GrantTypes.Remove(flow)); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest { |
||||
|
Code = "SplxlOBeZQQYbYS6WxSbIA", |
||||
|
GrantType = flow, |
||||
|
Username = "johndoe", |
||||
|
Password = "A3ddj3w", |
||||
|
RefreshToken = "8xLOxBtZp8" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.UnsupportedGrantType, response.Error); |
||||
|
Assert.Equal("The specified grant_type is not supported by this authorization server.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateTokenRequest_RequestWithOfflineAccessScopeIsRejectedWhenRefreshTokenFlowIsDisabled() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Configure(options => options.GrantTypes.Remove(OpenIdConnectConstants.GrantTypes.RefreshToken)); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest { |
||||
|
GrantType = OpenIdConnectConstants.GrantTypes.Password, |
||||
|
Username = "johndoe", |
||||
|
Password = "A3ddj3w", |
||||
|
Scope = OpenIdConnectConstants.Scopes.OfflineAccess |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("The 'offline_access' scope is not allowed.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateTokenRequest_ClientCredentialsRequestWithOfflineAccessScopeIsRejected() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest { |
||||
|
GrantType = OpenIdConnectConstants.GrantTypes.ClientCredentials, |
||||
|
Scope = OpenIdConnectConstants.Scopes.OfflineAccess |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("The 'offline_access' scope is not allowed when using grant_type=client_credentials.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData("client_id", "")] |
||||
|
[InlineData("", "client_secret")] |
||||
|
public async Task ValidateTokenRequest_ClientCredentialsRequestIsRejectedWhenCredentialsAreMissing(string identifier, string secret) { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = identifier, |
||||
|
ClientSecret = secret, |
||||
|
GrantType = OpenIdConnectConstants.GrantTypes.ClientCredentials |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("Client applications must be authenticated to use the client credentials grant.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateTokenRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(builder => builder.RequireClientIdentification()); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = null, |
||||
|
GrantType = OpenIdConnectConstants.GrantTypes.Password, |
||||
|
Username = "johndoe", |
||||
|
Password = "A3ddj3w" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("The mandatory 'client_id' parameter was missing.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateTokenRequest_RequestIsRejectedWhenClientCannotBeFound() { |
||||
|
// Arrange
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(null); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
GrantType = OpenIdConnectConstants.GrantTypes.Password, |
||||
|
Username = "johndoe", |
||||
|
Password = "A3ddj3w" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); |
||||
|
Assert.Equal("Application not found in the database: ensure that your client_id is correct.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateTokenRequest_ClientCredentialsRequestFromPublicClientIsRejected() { |
||||
|
// Arrange
|
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
||||
|
GrantType = OpenIdConnectConstants.GrantTypes.ClientCredentials |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.UnauthorizedClient, response.Error); |
||||
|
Assert.Equal("Public clients are not allowed to use the client credentials grant.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.GetClientTypeAsync(application), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateTokenRequest_ClientSecretCannotBeUsedByPublicClients() { |
||||
|
// Arrange
|
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
||||
|
GrantType = OpenIdConnectConstants.GrantTypes.Password, |
||||
|
Username = "johndoe", |
||||
|
Password = "A3ddj3w" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("Public clients are not allowed to send a client_secret.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.GetClientTypeAsync(application), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateTokenRequest_ClientSecretIsRequiredForConfidentialClients() { |
||||
|
// Arrange
|
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = null, |
||||
|
GrantType = OpenIdConnectConstants.GrantTypes.Password, |
||||
|
Username = "johndoe", |
||||
|
Password = "A3ddj3w" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); |
||||
|
Assert.Equal("Missing credentials: ensure that you specified a client_secret.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.GetClientTypeAsync(application), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateTokenRequest_RequestIsRejectedWhenClientCredentialsAreInvalid() { |
||||
|
// Arrange
|
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw")) |
||||
|
.ReturnsAsync(false); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
||||
|
GrantType = OpenIdConnectConstants.GrantTypes.Password, |
||||
|
Username = "johndoe", |
||||
|
Password = "A3ddj3w" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); |
||||
|
Assert.Equal("Invalid credentials: ensure that you specified a correct client_secret.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.GetClientTypeAsync(application), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationCodeIsExpired() { |
||||
|
// Arrange
|
||||
|
var ticket = new AuthenticationTicket( |
||||
|
new ClaimsPrincipal(), |
||||
|
new AuthenticationProperties(), |
||||
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
|
||||
|
ticket.SetPresenters("Fabrikam"); |
||||
|
ticket.SetTicketId("3E228451-1555-46F7-A471-951EFBA23A56"); |
||||
|
ticket.SetUsage(OpenIdConnectConstants.Usages.AuthorizationCode); |
||||
|
|
||||
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
||||
|
|
||||
|
format.Setup(mock => mock.Unprotect("SplxlOBeZQQYbYS6WxSbIA")) |
||||
|
.Returns(ticket); |
||||
|
|
||||
|
var manager = CreateTokenManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56")) |
||||
|
.ReturnsAsync(null); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.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 |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error); |
||||
|
Assert.Equal("The authorization code is no longer valid.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleTokenRequest_RequestIsRejectedWhenRefreshTokenIsExpired() { |
||||
|
// Arrange
|
||||
|
var ticket = new AuthenticationTicket( |
||||
|
new ClaimsPrincipal(), |
||||
|
new AuthenticationProperties(), |
||||
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
|
||||
|
ticket.SetTicketId("60FFF7EA-F98E-437B-937E-5073CC313103"); |
||||
|
ticket.SetUsage(OpenIdConnectConstants.Usages.RefreshToken); |
||||
|
|
||||
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
||||
|
|
||||
|
format.Setup(mock => mock.Unprotect("8xLOxBtZp8")) |
||||
|
.Returns(ticket); |
||||
|
|
||||
|
var manager = CreateTokenManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103")) |
||||
|
.ReturnsAsync(null); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public); |
||||
|
})); |
||||
|
|
||||
|
builder.Services.AddSingleton(manager); |
||||
|
|
||||
|
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 refresh token is no longer valid.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleTokenRequest_AuthorizationCodeIsAutomaticallyRevoked() { |
||||
|
// Arrange
|
||||
|
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur"); |
||||
|
|
||||
|
var ticket = new AuthenticationTicket( |
||||
|
new ClaimsPrincipal(identity), |
||||
|
new AuthenticationProperties(), |
||||
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
|
||||
|
ticket.SetPresenters("Fabrikam"); |
||||
|
ticket.SetTicketId("3E228451-1555-46F7-A471-951EFBA23A56"); |
||||
|
ticket.SetUsage(OpenIdConnectConstants.Usages.AuthorizationCode); |
||||
|
|
||||
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
||||
|
|
||||
|
format.Setup(mock => mock.Unprotect("SplxlOBeZQQYbYS6WxSbIA")) |
||||
|
.Returns(ticket); |
||||
|
|
||||
|
var token = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateTokenManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56")) |
||||
|
.ReturnsAsync(token); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.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 |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.RevokeAsync(token), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleTokenRequest_RefreshTokenIsAutomaticallyRevokedWhenSlidingExpirationIsEnabled() { |
||||
|
// Arrange
|
||||
|
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur"); |
||||
|
|
||||
|
var ticket = new AuthenticationTicket( |
||||
|
new ClaimsPrincipal(identity), |
||||
|
new AuthenticationProperties(), |
||||
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
|
||||
|
ticket.SetTicketId("60FFF7EA-F98E-437B-937E-5073CC313103"); |
||||
|
ticket.SetUsage(OpenIdConnectConstants.Usages.RefreshToken); |
||||
|
|
||||
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
||||
|
|
||||
|
format.Setup(mock => mock.Unprotect("8xLOxBtZp8")) |
||||
|
.Returns(ticket); |
||||
|
|
||||
|
var token = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateTokenManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103")) |
||||
|
.ReturnsAsync(token); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public); |
||||
|
})); |
||||
|
|
||||
|
builder.Services.AddSingleton(manager); |
||||
|
|
||||
|
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
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.RevokeAsync(token), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.ClientCredentials)] |
||||
|
[InlineData(OpenIdConnectConstants.GrantTypes.Password)] |
||||
|
[InlineData("urn:ietf:params:oauth:grant-type:custom_grant")] |
||||
|
public async Task HandleTokenRequest_RequestsAreNotHandledLocally(string flow) { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw")) |
||||
|
.ReturnsAsync(true); |
||||
|
})); |
||||
|
|
||||
|
builder.AllowCustomFlow("urn:ietf:params:oauth:grant-type:custom_grant"); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
||||
|
GrantType = flow, |
||||
|
Username = "johndoe", |
||||
|
Password = "A3ddj3w" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.NotNull(response.AccessToken); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,321 @@ |
|||||
|
using System.Linq; |
||||
|
using System.Security.Claims; |
||||
|
using System.Threading.Tasks; |
||||
|
using AspNet.Security.OpenIdConnect.Extensions; |
||||
|
using AspNet.Security.OpenIdConnect.Server; |
||||
|
using Microsoft.AspNetCore.Authentication; |
||||
|
using Microsoft.AspNetCore.Http.Authentication; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Moq; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace OpenIddict.Core.Tests.Infrastructure { |
||||
|
public partial class OpenIddictProviderTests { |
||||
|
[Fact] |
||||
|
public async Task ExtractIntrospectionRequest_GetRequestsAreRejected() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.GetAsync(IntrospectionEndpoint, new OpenIdConnectRequest { |
||||
|
Token = "2YotnFZFEjr1zCsicMWpAA" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("Introspection requests must use HTTP POST.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData("client_id", "")] |
||||
|
[InlineData("", "client_secret")] |
||||
|
public async Task ValidateIntrospectionRequest_ClientCredentialsRequestIsRejectedWhenCredentialsAreMissing(string identifier, string secret) { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = identifier, |
||||
|
ClientSecret = secret, |
||||
|
Token = "2YotnFZFEjr1zCsicMWpAA" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("Clients must be authenticated to use the introspection endpoint.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCannotBeFound() { |
||||
|
// Arrange
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(null); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
||||
|
Token = "2YotnFZFEjr1zCsicMWpAA" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); |
||||
|
Assert.Equal("Application not found in the database: ensure that your client_id is correct.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateIntrospectionRequest_RequestsSentByPublicClientsAreRejected() { |
||||
|
// Arrange
|
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
||||
|
Token = "2YotnFZFEjr1zCsicMWpAA" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); |
||||
|
Assert.Equal("Public applications are not allowed to use the introspection endpoint.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.GetClientTypeAsync(application), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCredentialsAreInvalid() { |
||||
|
// Arrange
|
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw")) |
||||
|
.ReturnsAsync(false); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
||||
|
Token = "2YotnFZFEjr1zCsicMWpAA" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); |
||||
|
Assert.Equal("Invalid credentials: ensure that you specified a correct client_secret.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.GetClientTypeAsync(application), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleIntrospectionRequest_RequestIsRejectedWhenClientIsNotAValidAudience() { |
||||
|
// Arrange
|
||||
|
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur"); |
||||
|
|
||||
|
var ticket = new AuthenticationTicket( |
||||
|
new ClaimsPrincipal(identity), |
||||
|
new AuthenticationProperties(), |
||||
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
|
||||
|
ticket.SetAudiences("Contoso"); |
||||
|
ticket.SetTicketId("3E228451-1555-46F7-A471-951EFBA23A56"); |
||||
|
ticket.SetUsage(OpenIdConnectConstants.Usages.AccessToken); |
||||
|
|
||||
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
||||
|
|
||||
|
format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA")) |
||||
|
.Returns(ticket); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw")) |
||||
|
.ReturnsAsync(true); |
||||
|
})); |
||||
|
|
||||
|
builder.Configure(options => options.AccessTokenFormat = format.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
||||
|
Token = "2YotnFZFEjr1zCsicMWpAA" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(1, response.Count()); |
||||
|
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleIntrospectionRequest_RequestIsRejectedWhenAuthorizationCodeIsRevoked() { |
||||
|
// Arrange
|
||||
|
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur"); |
||||
|
|
||||
|
var ticket = new AuthenticationTicket( |
||||
|
new ClaimsPrincipal(identity), |
||||
|
new AuthenticationProperties(), |
||||
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
|
||||
|
ticket.SetTicketId("3E228451-1555-46F7-A471-951EFBA23A56"); |
||||
|
ticket.SetUsage(OpenIdConnectConstants.Usages.AuthorizationCode); |
||||
|
|
||||
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
||||
|
|
||||
|
format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA")) |
||||
|
.Returns(ticket); |
||||
|
|
||||
|
var manager = CreateTokenManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56")) |
||||
|
.ReturnsAsync(null); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw")) |
||||
|
.ReturnsAsync(true); |
||||
|
})); |
||||
|
|
||||
|
builder.Services.AddSingleton(manager); |
||||
|
|
||||
|
builder.Configure(options => options.AuthorizationCodeFormat = format.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
||||
|
Token = "2YotnFZFEjr1zCsicMWpAA" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(1, response.Count()); |
||||
|
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleIntrospectionRequest_RequestIsRejectedWhenRefreshTokenIsRevoked() { |
||||
|
// Arrange
|
||||
|
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur"); |
||||
|
|
||||
|
var ticket = new AuthenticationTicket( |
||||
|
new ClaimsPrincipal(identity), |
||||
|
new AuthenticationProperties(), |
||||
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
|
||||
|
ticket.SetTicketId("3E228451-1555-46F7-A471-951EFBA23A56"); |
||||
|
ticket.SetUsage(OpenIdConnectConstants.Usages.RefreshToken); |
||||
|
|
||||
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
||||
|
|
||||
|
format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA")) |
||||
|
.Returns(ticket); |
||||
|
|
||||
|
var manager = CreateTokenManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56")) |
||||
|
.ReturnsAsync(null); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw")) |
||||
|
.ReturnsAsync(true); |
||||
|
})); |
||||
|
|
||||
|
builder.Services.AddSingleton(manager); |
||||
|
|
||||
|
builder.Configure(options => options.RefreshTokenFormat = format.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
||||
|
Token = "2YotnFZFEjr1zCsicMWpAA" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(1, response.Count()); |
||||
|
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56"), Times.Once()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,352 @@ |
|||||
|
using System; |
||||
|
using System.IdentityModel.Tokens.Jwt; |
||||
|
using System.Linq; |
||||
|
using System.Security.Claims; |
||||
|
using System.Threading.Tasks; |
||||
|
using AspNet.Security.OpenIdConnect.Extensions; |
||||
|
using AspNet.Security.OpenIdConnect.Server; |
||||
|
using Microsoft.AspNetCore.Authentication; |
||||
|
using Microsoft.AspNetCore.Http.Authentication; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.IdentityModel.Tokens; |
||||
|
using Moq; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace OpenIddict.Core.Tests.Infrastructure { |
||||
|
public partial class OpenIddictProviderTests { |
||||
|
[Theory] |
||||
|
[InlineData(OpenIdConnectConstants.TokenTypeHints.AccessToken)] |
||||
|
[InlineData(OpenIdConnectConstants.TokenTypeHints.IdToken)] |
||||
|
public async Task ValidateRevocationRequest_UnknownTokenTokenHintIsRejected(string hint) { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest { |
||||
|
Token = "SlAV32hkKG", |
||||
|
TokenTypeHint = hint |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.UnsupportedTokenType, response.Error); |
||||
|
Assert.Equal("Only authorization codes and refresh tokens can be revoked. When specifying a token_type_hint " + |
||||
|
"parameter, its value must be equal to 'authorization_code' or 'refresh_token'.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateRevocationRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(builder => builder.RequireClientIdentification()); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest { |
||||
|
Token = "SlAV32hkKG", |
||||
|
TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.RefreshToken |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("The mandatory 'client_id' parameter was missing.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateRevocationRequest_RequestIsRejectedWhenClientCannotBeFound() { |
||||
|
// Arrange
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(null); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
Token = "SlAV32hkKG", |
||||
|
TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.RefreshToken |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); |
||||
|
Assert.Equal("Application not found in the database: ensure that your client_id is correct.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateRevocationRequest_ClientSecretCannotBeUsedByPublicClients() { |
||||
|
// Arrange
|
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
||||
|
Token = "SlAV32hkKG", |
||||
|
TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.RefreshToken |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("Public clients are not allowed to send a client_secret.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.GetClientTypeAsync(application), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateRevocationRequest_ClientSecretIsRequiredForConfidentialClients() { |
||||
|
// Arrange
|
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = null, |
||||
|
Token = "SlAV32hkKG", |
||||
|
TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.RefreshToken |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); |
||||
|
Assert.Equal("Missing credentials: ensure that you specified a client_secret.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.GetClientTypeAsync(application), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateRevocationRequest_RequestIsRejectedWhenClientCredentialsAreInvalid() { |
||||
|
// Arrange
|
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw")) |
||||
|
.ReturnsAsync(false); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
||||
|
Token = "SlAV32hkKG", |
||||
|
TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.RefreshToken |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); |
||||
|
Assert.Equal("Invalid credentials: ensure that you specified a correct client_secret.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.GetClientTypeAsync(application), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleRevocationRequest_RequestIsRejectedWhenTokenIsAnAccessToken() { |
||||
|
// Arrange
|
||||
|
var ticket = new AuthenticationTicket( |
||||
|
new ClaimsPrincipal(), |
||||
|
new AuthenticationProperties(), |
||||
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
|
||||
|
ticket.SetTicketId("3E228451-1555-46F7-A471-951EFBA23A56"); |
||||
|
ticket.SetUsage(OpenIdConnectConstants.Usages.AccessToken); |
||||
|
|
||||
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
||||
|
|
||||
|
format.Setup(mock => mock.Unprotect("SlAV32hkKG")) |
||||
|
.Returns(ticket); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Configure(options => options.AccessTokenFormat = format.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest { |
||||
|
Token = "SlAV32hkKG" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.UnsupportedTokenType, response.Error); |
||||
|
Assert.Equal("Only authorization codes and refresh tokens can be revoked.", response.ErrorDescription); |
||||
|
|
||||
|
format.Verify(mock => mock.Unprotect("SlAV32hkKG"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleRevocationRequest_RequestIsNotRejectedWhenTokenIsAnIdentityToken() { |
||||
|
// Arrange
|
||||
|
var token = Mock.Of<SecurityToken>(mock => |
||||
|
mock.ValidFrom == DateTime.UtcNow.AddDays(-1) && |
||||
|
mock.ValidTo == DateTime.UtcNow.AddDays(1)); |
||||
|
|
||||
|
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
identity.AddClaim(OpenIdConnectConstants.Claims.Usage, OpenIdConnectConstants.Usages.IdentityToken); |
||||
|
|
||||
|
var handler = new Mock<JwtSecurityTokenHandler>(); |
||||
|
|
||||
|
handler.Setup(mock => mock.CanReadToken("SlAV32hkKG")) |
||||
|
.Returns(true); |
||||
|
|
||||
|
handler.As<ISecurityTokenValidator>() |
||||
|
.Setup(mock => mock.ValidateToken("SlAV32hkKG", It.IsAny<TokenValidationParameters>(), out token)) |
||||
|
.Returns(new ClaimsPrincipal(identity)); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Configure(options => options.IdentityTokenHandler = handler.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest { |
||||
|
Token = "SlAV32hkKG" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.UnsupportedTokenType, response.Error); |
||||
|
Assert.Equal("Only authorization codes and refresh tokens can be revoked.", response.ErrorDescription); |
||||
|
|
||||
|
handler.As<ISecurityTokenValidator>() |
||||
|
.Verify(mock => mock.CanReadToken("SlAV32hkKG"), Times.Once()); |
||||
|
|
||||
|
handler.As<ISecurityTokenValidator>() |
||||
|
.Verify(mock => mock.ValidateToken("SlAV32hkKG", It.IsAny<TokenValidationParameters>(), out token), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleRevocationRequest_TokenIsNotRevokedWhenItIsAlreadyInvalid() { |
||||
|
// Arrange
|
||||
|
var ticket = new AuthenticationTicket( |
||||
|
new ClaimsPrincipal(), |
||||
|
new AuthenticationProperties(), |
||||
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
|
||||
|
ticket.SetTicketId("3E228451-1555-46F7-A471-951EFBA23A56"); |
||||
|
ticket.SetUsage(OpenIdConnectConstants.Usages.RefreshToken); |
||||
|
|
||||
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
||||
|
|
||||
|
format.Setup(mock => mock.Unprotect("SlAV32hkKG")) |
||||
|
.Returns(ticket); |
||||
|
|
||||
|
var manager = CreateTokenManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56")) |
||||
|
.ReturnsAsync(null); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
|
||||
|
builder.Configure(options => options.AccessTokenFormat = format.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest { |
||||
|
Token = "SlAV32hkKG" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(0, response.Count()); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.RevokeAsync(It.IsAny<object>()), Times.Never()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleRevocationRequest_TokenIsSuccessfullyRevoked() { |
||||
|
// Arrange
|
||||
|
var ticket = new AuthenticationTicket( |
||||
|
new ClaimsPrincipal(), |
||||
|
new AuthenticationProperties(), |
||||
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
|
||||
|
ticket.SetTicketId("3E228451-1555-46F7-A471-951EFBA23A56"); |
||||
|
ticket.SetUsage(OpenIdConnectConstants.Usages.RefreshToken); |
||||
|
|
||||
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
||||
|
|
||||
|
format.Setup(mock => mock.Unprotect("SlAV32hkKG")) |
||||
|
.Returns(ticket); |
||||
|
|
||||
|
var token = Mock.Of<object>(); |
||||
|
|
||||
|
var manager = CreateTokenManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56")) |
||||
|
.ReturnsAsync(token); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
|
||||
|
builder.Configure(options => options.AccessTokenFormat = format.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest { |
||||
|
Token = "SlAV32hkKG" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(0, response.Count()); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56"), Times.Once()); |
||||
|
Mock.Get(manager).Verify(mock => mock.RevokeAsync(token), Times.Once()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
using AspNet.Security.OpenIdConnect.Extensions; |
||||
|
using Microsoft.Extensions.Caching.Distributed; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Moq; |
||||
|
using Newtonsoft.Json; |
||||
|
using Newtonsoft.Json.Bson; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace OpenIddict.Core.Tests.Infrastructure { |
||||
|
public partial class OpenIddictProviderTests { |
||||
|
[Fact] |
||||
|
public async Task SerializeAuthorizationCode_AuthorizationCodeIsAutomaticallyPersisted() { |
||||
|
// Arrange
|
||||
|
var request = new OpenIdConnectRequest { |
||||
|
ClientId = "Fabrikam", |
||||
|
RedirectUri = "http://www.fabrikam.com/path", |
||||
|
ResponseType = OpenIdConnectConstants.ResponseTypes.Code |
||||
|
}; |
||||
|
|
||||
|
var stream = new MemoryStream(); |
||||
|
using (var writer = new BsonWriter(stream)) { |
||||
|
writer.CloseOutput = false; |
||||
|
|
||||
|
var serializer = JsonSerializer.CreateDefault(); |
||||
|
serializer.Serialize(writer, request); |
||||
|
} |
||||
|
|
||||
|
var cache = new Mock<IDistributedCache>(); |
||||
|
|
||||
|
cache.Setup(mock => mock.GetAsync(OpenIddictConstants.Environment.AuthorizationRequest + |
||||
|
"b2ee7815-5579-4ff7-86b0-ba671b939d96")) |
||||
|
.ReturnsAsync(stream.ToArray()); |
||||
|
|
||||
|
var manager = CreateTokenManager(instance => { |
||||
|
instance.Setup(mock => mock.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode)) |
||||
|
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam")) |
||||
|
.ReturnsAsync(application); |
||||
|
|
||||
|
instance.Setup(mock => mock.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path")) |
||||
|
.ReturnsAsync(true); |
||||
|
|
||||
|
instance.Setup(mock => mock.GetClientTypeAsync(application)) |
||||
|
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public); |
||||
|
})); |
||||
|
|
||||
|
builder.Services.AddSingleton(manager); |
||||
|
|
||||
|
builder.Services.AddSingleton(cache.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest { |
||||
|
RequestId = "b2ee7815-5579-4ff7-86b0-ba671b939d96" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.NotNull(response.Code); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task SerializeRefreshToken_RefreshTokenIsAutomaticallyPersisted() { |
||||
|
// Arrange
|
||||
|
var manager = CreateTokenManager(instance => { |
||||
|
instance.Setup(mock => mock.CreateAsync(OpenIdConnectConstants.TokenTypeHints.RefreshToken)) |
||||
|
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest { |
||||
|
GrantType = OpenIdConnectConstants.GrantTypes.Password, |
||||
|
Username = "johndoe", |
||||
|
Password = "A3ddj3w", |
||||
|
Scope = OpenIdConnectConstants.Scopes.OfflineAccess |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.NotNull(response.RefreshToken); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.CreateAsync(OpenIdConnectConstants.TokenTypeHints.RefreshToken), Times.Once()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,112 @@ |
|||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using AspNet.Security.OpenIdConnect.Extensions; |
||||
|
using Microsoft.Extensions.Caching.Distributed; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Moq; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace OpenIddict.Core.Tests.Infrastructure { |
||||
|
public partial class OpenIddictProviderTests { |
||||
|
[Fact] |
||||
|
public async Task ExtractLogoutRequest_InvalidRequestIdParameterIsRejected() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest { |
||||
|
RequestId = "EFAF3596-F868-497F-96BB-AA2AD1F8B7E7" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); |
||||
|
Assert.Equal("Invalid request: timeout expired.", response.ErrorDescription); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ValidateLogoutRequest_RequestIsRejectedWhenRedirectUriIsInvalid() { |
||||
|
// Arrange
|
||||
|
var manager = CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByLogoutRedirectUri("http://www.fabrikam.com/path")) |
||||
|
.ReturnsAsync(null); |
||||
|
}); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(manager); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest { |
||||
|
PostLogoutRedirectUri = "http://www.fabrikam.com/path" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); |
||||
|
Assert.Equal("Invalid post_logout_redirect_uri.", response.ErrorDescription); |
||||
|
|
||||
|
Mock.Get(manager).Verify(mock => mock.FindByLogoutRedirectUri("http://www.fabrikam.com/path"), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task HandleLogoutRequest_RequestIsPersistedInDistributedCache() { |
||||
|
// Arrange
|
||||
|
var cache = new Mock<IDistributedCache>(); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
var application = Mock.Of<object>(); |
||||
|
|
||||
|
instance.Setup(mock => mock.FindByLogoutRedirectUri("http://www.fabrikam.com/path")) |
||||
|
.ReturnsAsync(application); |
||||
|
})); |
||||
|
|
||||
|
builder.Services.AddSingleton(cache.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest { |
||||
|
PostLogoutRedirectUri = "http://www.fabrikam.com/path" |
||||
|
}); |
||||
|
|
||||
|
var identifier = (string) response[OpenIdConnectConstants.Parameters.RequestId]; |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(1, response.Count()); |
||||
|
Assert.NotNull(identifier); |
||||
|
|
||||
|
cache.Verify(mock => mock.SetAsync( |
||||
|
OpenIddictConstants.Environment.LogoutRequest + identifier, |
||||
|
It.IsAny<byte[]>(), |
||||
|
It.IsAny<DistributedCacheEntryOptions>()), Times.Once()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ApplyLogoutResponse_ErroredRequestIsNotHandledLocallyWhenStatusCodeMiddlewareIsEnabled() { |
||||
|
// Arrange
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Services.AddSingleton(CreateApplicationManager(instance => { |
||||
|
instance.Setup(mock => mock.FindByLogoutRedirectUri("http://www.fabrikam.com/path")) |
||||
|
.ReturnsAsync(null); |
||||
|
})); |
||||
|
|
||||
|
builder.EnableAuthorizationEndpoint("/logout-status-code-middleware"); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync("/logout-status-code-middleware", new OpenIdConnectRequest { |
||||
|
PostLogoutRedirectUri = "http://www.fabrikam.com/path" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, (string) response["error_custom"]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
using System.Security.Claims; |
||||
|
using System.Threading.Tasks; |
||||
|
using AspNet.Security.OpenIdConnect.Extensions; |
||||
|
using AspNet.Security.OpenIdConnect.Server; |
||||
|
using Microsoft.AspNetCore.Authentication; |
||||
|
using Microsoft.AspNetCore.Http.Authentication; |
||||
|
using Moq; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace OpenIddict.Core.Tests.Infrastructure { |
||||
|
public partial class OpenIddictProviderTests { |
||||
|
[Fact] |
||||
|
public async Task HandleUserinfoRequest_RequestIsHandledByUserCode() { |
||||
|
// Arrange
|
||||
|
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur"); |
||||
|
|
||||
|
var ticket = new AuthenticationTicket( |
||||
|
new ClaimsPrincipal(identity), |
||||
|
new AuthenticationProperties(), |
||||
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
|
||||
|
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(); |
||||
|
|
||||
|
format.Setup(mock => mock.Unprotect("SlAV32hkKG")) |
||||
|
.Returns(ticket); |
||||
|
|
||||
|
var server = CreateAuthorizationServer(builder => { |
||||
|
builder.Configure(options => options.AccessTokenFormat = format.Object); |
||||
|
}); |
||||
|
|
||||
|
var client = new OpenIdConnectClient(server.CreateClient()); |
||||
|
|
||||
|
// Act
|
||||
|
var response = await client.PostAsync(UserinfoEndpoint, new OpenIdConnectRequest { |
||||
|
AccessToken = "SlAV32hkKG" |
||||
|
}); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal("Bob le Bricoleur", (string) response[OpenIdConnectConstants.Claims.Subject]); |
||||
|
|
||||
|
format.Verify(mock => mock.Unprotect("SlAV32hkKG"), Times.Once()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,150 @@ |
|||||
|
using System; |
||||
|
using System.Reflection; |
||||
|
using System.Security.Claims; |
||||
|
using System.Threading.Tasks; |
||||
|
using AspNet.Security.OpenIdConnect.Extensions; |
||||
|
using AspNet.Security.OpenIdConnect.Server; |
||||
|
using Microsoft.AspNetCore.Authentication; |
||||
|
using Microsoft.AspNetCore.Builder; |
||||
|
using Microsoft.AspNetCore.DataProtection; |
||||
|
using Microsoft.AspNetCore.Diagnostics; |
||||
|
using Microsoft.AspNetCore.Hosting; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.AspNetCore.Http.Authentication; |
||||
|
using Microsoft.AspNetCore.TestHost; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Net.Http.Headers; |
||||
|
using Moq; |
||||
|
using Newtonsoft.Json; |
||||
|
|
||||
|
namespace OpenIddict.Core.Tests.Infrastructure { |
||||
|
public partial class OpenIddictProviderTests { |
||||
|
public const string AuthorizationEndpoint = "/connect/authorize"; |
||||
|
public const string ConfigurationEndpoint = "/.well-known/openid-configuration"; |
||||
|
public const string IntrospectionEndpoint = "/connect/introspect"; |
||||
|
public const string LogoutEndpoint = "/connect/logout"; |
||||
|
public const string RevocationEndpoint = "/connect/revoke"; |
||||
|
public const string TokenEndpoint = "/connect/token"; |
||||
|
public const string UserinfoEndpoint = "/connect/userinfo"; |
||||
|
|
||||
|
private static TestServer CreateAuthorizationServer(Action<OpenIddictBuilder> configuration = null) { |
||||
|
var builder = new WebHostBuilder(); |
||||
|
|
||||
|
builder.UseEnvironment("Testing"); |
||||
|
|
||||
|
builder.ConfigureLogging(options => options.AddDebug()); |
||||
|
|
||||
|
builder.ConfigureServices(services => { |
||||
|
var instance = services.AddOpenIddict<object, object, object, object>() |
||||
|
// Disable the transport security requirement during testing.
|
||||
|
.DisableHttpsRequirement() |
||||
|
|
||||
|
// Enable the tested endpoints.
|
||||
|
.EnableAuthorizationEndpoint(AuthorizationEndpoint) |
||||
|
.EnableIntrospectionEndpoint(IntrospectionEndpoint) |
||||
|
.EnableLogoutEndpoint(LogoutEndpoint) |
||||
|
.EnableRevocationEndpoint(RevocationEndpoint) |
||||
|
.EnableTokenEndpoint(TokenEndpoint) |
||||
|
.EnableUserinfoEndpoint(UserinfoEndpoint) |
||||
|
|
||||
|
// Enable the tested flows.
|
||||
|
.AllowAuthorizationCodeFlow() |
||||
|
.AllowClientCredentialsFlow() |
||||
|
.AllowImplicitFlow() |
||||
|
.AllowPasswordFlow() |
||||
|
.AllowRefreshTokenFlow() |
||||
|
|
||||
|
// Register the X.509 certificate used to sign the identity tokens.
|
||||
|
.AddSigningCertificate( |
||||
|
assembly: typeof(OpenIddictProviderTests).GetTypeInfo().Assembly, |
||||
|
resource: "OpenIddict.Core.Tests.Certificate.pfx", |
||||
|
password: "OpenIddict") |
||||
|
|
||||
|
// Note: overriding the default data protection provider is not necessary for the tests to pass,
|
||||
|
// but is useful to ensure unnecessary keys are not persisted in testing environments, which also
|
||||
|
// helps make the unit tests run faster, as no registry or disk access is required in this case.
|
||||
|
.UseDataProtectionProvider(new EphemeralDataProtectionProvider()); |
||||
|
|
||||
|
// Run the configuration delegate
|
||||
|
// registered by the unit tests.
|
||||
|
configuration?.Invoke(instance); |
||||
|
}); |
||||
|
|
||||
|
builder.Configure(app => { |
||||
|
app.UseStatusCodePages(context => { |
||||
|
context.HttpContext.Response.Headers[HeaderNames.ContentType] = "application/json"; |
||||
|
|
||||
|
return context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(new { |
||||
|
error_custom = OpenIdConnectConstants.Errors.InvalidRequest |
||||
|
})); |
||||
|
}); |
||||
|
|
||||
|
app.Use(next => context => { |
||||
|
if (context.Request.Path != "/authorize-status-code-middleware" && |
||||
|
context.Request.Path != "/logout-status-code-middleware") { |
||||
|
var feature = context.Features.Get<IStatusCodePagesFeature>(); |
||||
|
feature.Enabled = false; |
||||
|
} |
||||
|
|
||||
|
return next(context); |
||||
|
}); |
||||
|
|
||||
|
app.UseOpenIddict(); |
||||
|
|
||||
|
app.Run(context => { |
||||
|
if (context.Request.Path == AuthorizationEndpoint || |
||||
|
context.Request.Path == TokenEndpoint) { |
||||
|
var request = context.GetOpenIdConnectRequest(); |
||||
|
|
||||
|
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Magnifique"); |
||||
|
|
||||
|
var ticket = new AuthenticationTicket( |
||||
|
new ClaimsPrincipal(identity), |
||||
|
new AuthenticationProperties(), |
||||
|
OpenIdConnectServerDefaults.AuthenticationScheme); |
||||
|
|
||||
|
ticket.SetScopes(request.GetScopes()); |
||||
|
|
||||
|
return context.Authentication.SignInAsync(ticket.AuthenticationScheme, ticket.Principal, ticket.Properties); |
||||
|
} |
||||
|
|
||||
|
else if (context.Request.Path == UserinfoEndpoint) { |
||||
|
context.Response.Headers[HeaderNames.ContentType] = "application/json"; |
||||
|
|
||||
|
return context.Response.WriteAsync(JsonConvert.SerializeObject(new { |
||||
|
sub = "Bob le Bricoleur" |
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
return Task.FromResult(0); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
return new TestServer(builder); |
||||
|
} |
||||
|
|
||||
|
private static OpenIddictApplicationManager<object> CreateApplicationManager(Action<Mock<OpenIddictApplicationManager<object>>> setup = null) { |
||||
|
var manager = new Mock<OpenIddictApplicationManager<object>>( |
||||
|
Mock.Of<IServiceProvider>(), |
||||
|
Mock.Of<IOpenIddictApplicationStore<object>>(), |
||||
|
Mock.Of<ILogger<OpenIddictApplicationManager<object>>>()); |
||||
|
|
||||
|
setup?.Invoke(manager); |
||||
|
|
||||
|
return manager.Object; |
||||
|
} |
||||
|
|
||||
|
private static OpenIddictTokenManager<object> CreateTokenManager(Action<Mock<OpenIddictTokenManager<object>>> setup = null) { |
||||
|
var manager = new Mock<OpenIddictTokenManager<object>>( |
||||
|
Mock.Of<IServiceProvider>(), |
||||
|
Mock.Of<IOpenIddictTokenStore<object>>(), |
||||
|
Mock.Of<ILogger<OpenIddictTokenManager<object>>>()); |
||||
|
|
||||
|
setup?.Invoke(manager); |
||||
|
|
||||
|
return manager.Object; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,3 +0,0 @@ |
|||||
namespace OpenIddict.Core.Tests { |
|
||||
public class Placeholder { } |
|
||||
} |
|
||||
Loading…
Reference in new issue