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