Browse Source

Introduce a new DisableTokenRevocation option

pull/308/head
Kévin Chalet 9 years ago
parent
commit
1478905dc9
  1. 19
      src/OpenIddict/OpenIddictExtensions.cs
  2. 7
      src/OpenIddict/OpenIddictOptions.cs
  3. 75
      src/OpenIddict/OpenIddictProvider.Exchange.cs
  4. 4
      src/OpenIddict/OpenIddictProvider.Introspection.cs
  5. 2
      src/OpenIddict/OpenIddictProvider.Revocation.cs
  6. 39
      src/OpenIddict/OpenIddictProvider.Serialization.cs
  7. 71
      test/OpenIddict.Tests/OpenIddictExtensionsTests.cs
  8. 93
      test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs
  9. 105
      test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs

19
src/OpenIddict/OpenIddictExtensions.cs

@ -95,6 +95,10 @@ namespace Microsoft.AspNetCore.Builder {
"client credentials, password and refresh token flows.");
}
if (options.RevocationEndpointPath.HasValue && options.DisableTokenRevocation) {
throw new InvalidOperationException("The revocation endpoint cannot be enabled when token revocation is disabled.");
}
return app.UseOpenIdConnectServer(options);
}
@ -479,6 +483,21 @@ namespace Microsoft.AspNetCore.Builder {
return builder.Configure(options => options.UseSlidingExpiration = false);
}
/// <summary>
/// Disables token revocation, so that authorization code and
/// refresh tokens are never stored and cannot be revoked.
/// Using this option is generally not recommended.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder DisableTokenRevocation([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}
return builder.Configure(options => options.DisableTokenRevocation = true);
}
/// <summary>
/// Enables the authorization endpoint.
/// </summary>

7
src/OpenIddict/OpenIddictOptions.cs

@ -28,6 +28,13 @@ namespace OpenIddict {
/// </summary>
public IDistributedCache Cache { get; set; }
/// <summary>
/// Gets or sets a boolean indicating whether token revocation should be disabled.
/// When disabled, authorization code and refresh tokens are not stored
/// and cannot be revoked. Using this option is generally not recommended.
/// </summary>
public bool DisableTokenRevocation { get; set; }
/// <summary>
/// Gets or sets a boolean indicating whether request caching should be enabled.
/// When enabled, both authorization and logout requests are automatically stored

75
src/OpenIddict/OpenIddictProvider.Exchange.cs

@ -182,58 +182,55 @@ namespace OpenIddict {
}
public override async Task HandleTokenRequest([NotNull] HandleTokenRequestContext context) {
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
var tokens = context.HttpContext.RequestServices.GetRequiredService<OpenIddictTokenManager<TToken>>();
if (context.Request.IsAuthorizationCodeGrantType()) {
if (!options.Value.DisableTokenRevocation && (context.Request.IsAuthorizationCodeGrantType() ||
context.Request.IsRefreshTokenGrantType())) {
Debug.Assert(context.Ticket != null, "The authentication ticket shouldn't be null.");
// Extract the token identifier from the authorization code.
// Extract the token identifier from the authentication ticket.
var identifier = context.Ticket.GetTicketId();
Debug.Assert(!string.IsNullOrEmpty(identifier),
"The authorization code should contain a ticket identifier.");
"The authentication ticket should contain a ticket identifier.");
// Retrieve the token from the database and ensure it is still valid.
var token = await tokens.FindByIdAsync(identifier, context.HttpContext.RequestAborted);
if (token == null) {
logger.LogError("The token request was rejected because the authorization code was revoked.");
if (context.Request.IsAuthorizationCodeGrantType()) {
// Retrieve the token from the database and ensure it is still valid.
var token = await tokens.FindByIdAsync(identifier, context.HttpContext.RequestAborted);
if (token == null) {
logger.LogError("The token request was rejected because the authorization code was revoked.");
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "The authorization code is no longer valid.");
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "The authorization code is no longer valid.");
return;
}
return;
}
// Revoke the authorization code to prevent token reuse.
await tokens.RevokeAsync(token, context.HttpContext.RequestAborted);
}
else if (context.Request.IsRefreshTokenGrantType()) {
Debug.Assert(context.Ticket != null, "The authentication ticket shouldn't be null.");
// Extract the token identifier from the refresh token.
var identifier = context.Ticket.GetTicketId();
Debug.Assert(!string.IsNullOrEmpty(identifier),
"The refresh token should contain a ticket identifier.");
// Retrieve the token from the database and ensure it is still valid.
var token = await tokens.FindByIdAsync(identifier, context.HttpContext.RequestAborted);
if (token == null) {
logger.LogError("The token request was rejected because the refresh token was revoked.");
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "The refresh token is no longer valid.");
return;
// Revoke the authorization code to prevent token reuse.
await tokens.RevokeAsync(token, context.HttpContext.RequestAborted);
}
// When sliding expiration is enabled, immediately
// revoke the refresh token to prevent future reuse.
// See https://tools.ietf.org/html/rfc6749#section-6.
if (context.Options.UseSlidingExpiration) {
await tokens.RevokeAsync(token, context.HttpContext.RequestAborted);
else if (context.Request.IsRefreshTokenGrantType()) {
// Retrieve the token from the database and ensure it is still valid.
var token = await tokens.FindByIdAsync(identifier, context.HttpContext.RequestAborted);
if (token == null) {
logger.LogError("The token request was rejected because the refresh token was revoked.");
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "The refresh token is no longer valid.");
return;
}
// When sliding expiration is enabled, immediately
// revoke the refresh token to prevent future reuse.
// See https://tools.ietf.org/html/rfc6749#section-6.
if (context.Options.UseSlidingExpiration) {
await tokens.RevokeAsync(token, context.HttpContext.RequestAborted);
}
}
}

4
src/OpenIddict/OpenIddictProvider.Introspection.cs

@ -13,6 +13,7 @@ using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OpenIddict.Core;
namespace OpenIddict {
@ -89,6 +90,7 @@ namespace OpenIddict {
}
public override async Task HandleIntrospectionRequest([NotNull] HandleIntrospectionRequestContext context) {
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
var tokens = context.HttpContext.RequestServices.GetRequiredService<OpenIddictTokenManager<TToken>>();
@ -110,7 +112,7 @@ namespace OpenIddict {
}
// When the received ticket is revocable, ensure it is still valid.
if (context.Ticket.IsAuthorizationCode() || context.Ticket.IsRefreshToken()) {
if (!options.Value.DisableTokenRevocation && (context.Ticket.IsAuthorizationCode() || context.Ticket.IsRefreshToken())) {
// Retrieve the token from the database using the unique identifier stored in the authentication ticket:
// if the corresponding entry cannot be found, return Active = false to indicate that is is no longer valid.
var token = await tokens.FindByIdAsync(context.Ticket.GetTicketId(), context.HttpContext.RequestAborted);

2
src/OpenIddict/OpenIddictProvider.Revocation.cs

@ -23,6 +23,8 @@ namespace OpenIddict {
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
Debug.Assert(!options.Value.DisableTokenRevocation, "Token revocation support shouldn't be disabled at this stage.");
// When token_type_hint is specified, reject the request if it doesn't correspond to a revocable token.
if (!string.IsNullOrEmpty(context.Request.TokenTypeHint) &&
!string.Equals(context.Request.TokenTypeHint, OpenIdConnectConstants.TokenTypeHints.AuthorizationCode) &&

39
src/OpenIddict/OpenIddictProvider.Serialization.cs

@ -11,37 +11,44 @@ using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Core;
namespace OpenIddict {
public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task SerializeAuthorizationCode([NotNull] SerializeAuthorizationCodeContext context) {
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
var tokens = context.HttpContext.RequestServices.GetRequiredService<OpenIddictTokenManager<TToken>>();
var identifier = await tokens.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, context.HttpContext.RequestAborted);
if (string.IsNullOrEmpty(identifier)) {
throw new InvalidOperationException("The unique key associated with an authorization code cannot be null or empty.");
}
if (!options.Value.DisableTokenRevocation) {
var identifier = await tokens.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, context.HttpContext.RequestAborted);
if (string.IsNullOrEmpty(identifier)) {
throw new InvalidOperationException("The unique key associated with an authorization code cannot be null or empty.");
}
// Attach the key returned by the underlying store
// to the authorization code to override the default GUID
// generated by the OpenID Connect server middleware.
context.Ticket.SetTicketId(identifier);
// Attach the key returned by the underlying store
// to the authorization code to override the default GUID
// generated by the OpenID Connect server middleware.
context.Ticket.SetTicketId(identifier);
}
}
public override async Task SerializeRefreshToken([NotNull] SerializeRefreshTokenContext context) {
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
var tokens = context.HttpContext.RequestServices.GetRequiredService<OpenIddictTokenManager<TToken>>();
var identifier = await tokens.CreateAsync(OpenIdConnectConstants.TokenTypeHints.RefreshToken, context.HttpContext.RequestAborted);
if (string.IsNullOrEmpty(identifier)) {
throw new InvalidOperationException("The unique key associated with a refresh token cannot be null or empty.");
}
if (!options.Value.DisableTokenRevocation) {
var identifier = await tokens.CreateAsync(OpenIdConnectConstants.TokenTypeHints.RefreshToken, context.HttpContext.RequestAborted);
if (string.IsNullOrEmpty(identifier)) {
throw new InvalidOperationException("The unique key associated with a refresh token cannot be null or empty.");
}
// Attach the key returned by the underlying store
// to the refresh token to override the default GUID
// generated by the OpenID Connect server middleware.
context.Ticket.SetTicketId(identifier);
// Attach the key returned by the underlying store
// to the refresh token to override the default GUID
// generated by the OpenID Connect server middleware.
context.Ticket.SetTicketId(identifier);
}
}
}
}

71
test/OpenIddict.Tests/OpenIddictExtensionsTests.cs

@ -15,7 +15,7 @@ using Xunit;
namespace OpenIddict.Tests {
public class OpenIddictExtensionsTests {
[Fact]
public void UseOpenIddict_AnExceptionIsThrownWhenServicesAreNotRegistered() {
public void UseOpenIddict_ThrowsAnExceptionWhenServicesAreNotRegistered() {
// Arrange
var services = new ServiceCollection();
@ -29,7 +29,7 @@ namespace OpenIddict.Tests {
}
[Fact]
public void UseOpenIddict_AnExceptionIsThrownWhenNoDistributedCacheIsRegisteredIfRequestCachingIsEnabled() {
public void UseOpenIddict_ThrowsAnExceptionWhenNoDistributedCacheIsRegisteredIfRequestCachingIsEnabled() {
// Arrange
var services = new ServiceCollection();
@ -46,7 +46,7 @@ namespace OpenIddict.Tests {
}
[Fact]
public void UseOpenIddict_AnExceptionIsThrownWhenNoSigningCredentialsIsRegistered() {
public void UseOpenIddict_ThrowsAnExceptionWhenNoSigningCredentialsIsRegistered() {
// Arrange
var services = new ServiceCollection();
services.AddOpenIddict();
@ -62,7 +62,7 @@ namespace OpenIddict.Tests {
}
[Fact]
public void UseOpenIddict_AnExceptionIsThrownWhenNoFlowIsEnabled() {
public void UseOpenIddict_ThrowsAnExceptionWhenNoFlowIsEnabled() {
// Arrange
var services = new ServiceCollection();
@ -83,7 +83,7 @@ namespace OpenIddict.Tests {
[Theory]
[InlineData(OpenIdConnectConstants.GrantTypes.AuthorizationCode)]
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit)]
public void UseOpenIddict_AnExceptionIsThrownWhenAuthorizationEndpointIsDisabled(string flow) {
public void UseOpenIddict_ThrowsAnExceptionWhenAuthorizationEndpointIsDisabled(string flow) {
// Arrange
var services = new ServiceCollection();
@ -109,7 +109,7 @@ namespace OpenIddict.Tests {
[InlineData(OpenIdConnectConstants.GrantTypes.ClientCredentials)]
[InlineData(OpenIdConnectConstants.GrantTypes.Password)]
[InlineData(OpenIdConnectConstants.GrantTypes.RefreshToken)]
public void UseOpenIddict_AnExceptionIsThrownWhenTokenEndpointIsDisabled(string flow) {
public void UseOpenIddict_ThrowsAnExceptionWhenTokenEndpointIsDisabled(string flow) {
// Arrange
var services = new ServiceCollection();
@ -131,6 +131,29 @@ namespace OpenIddict.Tests {
"client credentials, password and refresh token flows.", exception.Message);
}
[Fact]
public void UseOpenIddict_ThrowsAnExceptionWhenTokenRevocationIsDisabled() {
// Arrange
var services = new ServiceCollection();
services.AddOpenIddict()
.AddSigningCertificate(
assembly: typeof(OpenIddictProviderTests).GetTypeInfo().Assembly,
resource: "OpenIddict.Tests.Certificate.pfx",
password: "OpenIddict")
.EnableAuthorizationEndpoint("/connect/authorize")
.EnableRevocationEndpoint("/connect/revocation")
.AllowImplicitFlow()
.DisableTokenRevocation();
var builder = new ApplicationBuilder(services.BuildServiceProvider());
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(() => builder.UseOpenIddict());
Assert.Equal("The revocation endpoint cannot be enabled when token revocation is disabled.", exception.Message);
}
[Fact]
public void Configure_OptionsAreCorrectlyAmended() {
// Arrange
@ -413,6 +436,42 @@ namespace OpenIddict.Tests {
Assert.Equal(PathString.Empty, options.Value.CryptographyEndpointPath);
}
[Fact]
public void DisableSlidingExpiration_SlidingExpirationIsDisabled() {
// Arrange
var services = new ServiceCollection();
services.AddOptions();
var builder = new OpenIddictBuilder(services);
// Act
builder.DisableSlidingExpiration();
var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptions<OpenIddictOptions>>();
// Assert
Assert.False(options.Value.UseSlidingExpiration);
}
[Fact]
public void DisableTokenRevocation_TokenRevocationIsDisabled() {
// Arrange
var services = new ServiceCollection();
services.AddOptions();
var builder = new OpenIddictBuilder(services);
// Act
builder.DisableTokenRevocation();
var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptions<OpenIddictOptions>>();
// Assert
Assert.True(options.Value.DisableTokenRevocation);
}
[Fact]
public void EnableAuthorizationEndpoint_AuthorizationEndpointIsEnabled() {
// Arrange

93
test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs

@ -7,6 +7,7 @@ using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.Extensions.DependencyInjection;
using Moq;
@ -299,6 +300,98 @@ namespace OpenIddict.Tests {
Mock.Get(manager).Verify(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task HandleTokenRequest_AuthorizationCodeRevocationIsIgnoredWhenTokenRevocationIsDisabled() {
// 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 server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public);
}));
builder.Configure(options => options.AuthorizationCodeFormat = format.Object);
builder.Configure(options => options.RevocationEndpointPath = PathString.Empty);
builder.DisableTokenRevocation();
});
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.NotNull(response.AccessToken);
}
[Fact]
public async Task HandleTokenRequest_RefreshTokenRevocationIsIgnoredWhenTokenRevocationIsDisabled() {
// 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 server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public);
}));
builder.Configure(options => options.RefreshTokenFormat = format.Object);
builder.Configure(options => options.RevocationEndpointPath = PathString.Empty);
builder.DisableTokenRevocation();
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
GrantType = OpenIdConnectConstants.GrantTypes.RefreshToken,
RefreshToken = "8xLOxBtZp8"
});
// Assert
Assert.NotNull(response.AccessToken);
}
[Fact]
public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationCodeIsExpired() {
// Arrange

105
test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs

@ -8,6 +8,7 @@ using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.Extensions.DependencyInjection;
using Moq;
@ -206,6 +207,110 @@ namespace OpenIddict.Tests {
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
}
[Fact]
public async Task HandleIntrospectionRequest_AuthorizationCodeRevocationIsIgnoredWhenTokenRevocationIsDisabled() {
// 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 server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
instance.Setup(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
builder.Configure(options => options.AuthorizationCodeFormat = format.Object);
builder.Configure(options => options.RevocationEndpointPath = PathString.Empty);
builder.DisableTokenRevocation();
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest {
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"
});
// Assert
Assert.True((bool) response[OpenIdConnectConstants.Claims.Active]);
}
[Fact]
public async Task HandleIntrospectionRequest_RefreshTokenRevocationIsIgnoredWhenTokenRevocationIsDisabled() {
// 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 server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
instance.Setup(mock => mock.ValidateSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
builder.Configure(options => options.AuthorizationCodeFormat = format.Object);
builder.Configure(options => options.RevocationEndpointPath = PathString.Empty);
builder.DisableTokenRevocation();
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest {
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"
});
// Assert
Assert.True((bool) response[OpenIdConnectConstants.Claims.Active]);
}
[Fact]
public async Task HandleIntrospectionRequest_RequestIsRejectedWhenAuthorizationCodeIsRevoked() {
// Arrange

Loading…
Cancel
Save