diff --git a/src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx b/src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx
index 00b89ba1..f53a180c 100644
--- a/src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx
+++ b/src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx
@@ -396,12 +396,6 @@ Consider using 'options.AddSigningCredentials(SigningCredentials)' instead.
The verification endpoint must be enabled to use the device flow.
-
- The device and verification endpoints cannot be enabled when token storage is disabled.
-
-
- The revocation endpoint cannot be enabled when token storage is disabled.
-
Reference tokens cannot be used when disabling token storage.
diff --git a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs
index d4085eb3..3425a983 100644
--- a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs
+++ b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs
@@ -34,6 +34,20 @@ namespace OpenIddict.Server
throw new ArgumentNullException(nameof(options));
}
+ if (options.EnableDegradedMode)
+ {
+ // Explicitly disable all the features that are implicitly excluded when the degraded mode is active.
+ options.DisableAuthorizationStorage = options.DisableTokenStorage = true;
+ options.IgnoreEndpointPermissions = options.IgnoreGrantTypePermissions = options.IgnoreScopePermissions = true;
+ options.UseReferenceAccessTokens = options.UseReferenceRefreshTokens = false;
+
+ // When the degraded mode is enabled (and the token storage disabled), OpenIddict is not able to dynamically
+ // update the expiration date of a token. As such, either rolling tokens MUST be enabled or sliding token
+ // expiration MUST be disabled to always issue new refresh tokens with the same fixed expiration date.
+ // By default, OpenIddict will automatically force the rolling tokens option when using the degraded mode.
+ options.UseRollingRefreshTokens |= !options.UseRollingRefreshTokens && !options.DisableSlidingRefreshTokenExpiration;
+ }
+
if (options.JsonWebTokenHandler == null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID1074));
@@ -78,16 +92,6 @@ namespace OpenIddict.Server
if (options.DisableTokenStorage)
{
- if (options.DeviceEndpointUris.Count != 0 || options.VerificationEndpointUris.Count != 0)
- {
- throw new InvalidOperationException(SR.GetResourceString(SR.ID1080));
- }
-
- if (options.RevocationEndpointUris.Count != 0)
- {
- throw new InvalidOperationException(SR.GetResourceString(SR.ID1081));
- }
-
if (options.UseReferenceAccessTokens || options.UseReferenceRefreshTokens)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID1082));
diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs
index 54d00fe9..a5365970 100644
--- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs
+++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs
@@ -2192,7 +2192,7 @@ namespace OpenIddict.Server.IntegrationTests
}
[Fact]
- public async Task ProcessSignIn_NoRefreshTokenIsReturnedForRefreshTokenGrantRequests()
+ public async Task ProcessSignIn_ARefreshTokenIsReturnedForRefreshTokenGrantRequests()
{
// Arrange
await using var server = await CreateServerAsync(options =>
@@ -2221,60 +2221,7 @@ namespace OpenIddict.Server.IntegrationTests
{
builder.UseInlineHandler(context =>
{
- Assert.False(context.IncludeRefreshToken);
-
- return default;
- });
-
- builder.SetOrder(EvaluateReturnedTokens.Descriptor.Order + 500);
- });
- });
-
- await using var client = await server.CreateClientAsync();
-
- // Act
- var response = await client.PostAsync("/connect/token", new OpenIddictRequest
- {
- GrantType = GrantTypes.RefreshToken,
- RefreshToken = "8xLOxBtZp8"
- });
-
- // Assert
- Assert.Null(response.RefreshToken);
- }
-
- [Fact]
- public async Task ProcessSignIn_NoRefreshTokenIsReturnedWhenSlidingExpirationIsDisabled()
- {
- // Arrange
- await using var server = await CreateServerAsync(options =>
- {
- options.EnableDegradedMode();
- options.DisableSlidingRefreshTokenExpiration();
-
- options.AddEventHandler(builder =>
- {
- builder.UseInlineHandler(context =>
- {
- Assert.Equal("8xLOxBtZp8", context.Token);
- Assert.Equal(TokenTypeHints.RefreshToken, context.TokenType);
-
- context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
- .SetTokenType(TokenTypeHints.RefreshToken)
- .SetScopes(Scopes.OfflineAccess)
- .SetClaim(Claims.Subject, "Bob le Bricoleur");
-
- return default;
- });
-
- builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
- });
-
- options.AddEventHandler(builder =>
- {
- builder.UseInlineHandler(context =>
- {
- Assert.False(context.IncludeRefreshToken);
+ Assert.True(context.IncludeRefreshToken);
return default;
});
@@ -2293,7 +2240,7 @@ namespace OpenIddict.Server.IntegrationTests
});
// Assert
- Assert.Null(response.RefreshToken);
+ Assert.NotNull(response.RefreshToken);
}
[Fact]
@@ -3003,6 +2950,7 @@ namespace OpenIddict.Server.IntegrationTests
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
+ options.DisableSlidingRefreshTokenExpiration();
options.AddEventHandler(builder =>
{