diff --git a/src/OpenIddict.Server/OpenIddictServerEvents.Protection.cs b/src/OpenIddict.Server/OpenIddictServerEvents.Protection.cs
index 0781c1aa..4348dfcd 100644
--- a/src/OpenIddict.Server/OpenIddictServerEvents.Protection.cs
+++ b/src/OpenIddict.Server/OpenIddictServerEvents.Protection.cs
@@ -29,14 +29,32 @@ namespace OpenIddict.Server
}
///
- /// Gets or sets the request.
+ /// Gets or sets the request, or null if it is not available.
///
- public OpenIddictRequest Request
+ public OpenIddictRequest? Request
{
- get => Transaction.Request!;
+ get => Transaction.Request;
set => Transaction.Request = value;
}
+ ///
+ /// Gets or sets the client identifier of the application
+ /// the resulting token will be issued to, if applicable.
+ ///
+ public string? ClientId { get; set; }
+
+ ///
+ /// Gets or sets a boolean indicating whether a token entry
+ /// should be created to persist token metadata in a database.
+ ///
+ public bool CreateTokenEntry { get; set; }
+
+ ///
+ /// Gets or sets a boolean indicating whether the token payload
+ /// should be persisted alongside the token metadata in the database.
+ ///
+ public bool PersistTokenPayload { get; set; }
+
///
/// Gets or sets the security principal used to create the token.
///
@@ -82,14 +100,19 @@ namespace OpenIddict.Server
}
///
- /// Gets or sets the request.
+ /// Gets or sets the request, or null if it is not available.
///
- public OpenIddictRequest Request
+ public OpenIddictRequest? Request
{
- get => Transaction.Request!;
+ get => Transaction.Request;
set => Transaction.Request = value;
}
+ ///
+ /// Gets or sets a boolean indicating whether lifetime validation is disabled.
+ ///
+ public bool DisableLifetimeValidation { get; set; }
+
///
/// Gets or sets the security token handler used to validate the token.
///
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
index 326448df..e1b4935a 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
@@ -72,7 +72,12 @@ namespace OpenIddict.Server
/*
* Token request handling:
*/
- AttachPrincipal.Descriptor);
+ AttachPrincipal.Descriptor,
+
+ /*
+ * Token response handling:
+ */
+ NormalizeErrorResponse.Descriptor);
///
/// Contains the logic responsible of extracting token requests and invoking the corresponding event handlers.
@@ -1716,6 +1721,56 @@ namespace OpenIddict.Server
return default;
}
}
+
+ ///
+ /// Contains the logic responsible of converting token errors to standard invalid_grant responses.
+ ///
+ public class NormalizeErrorResponse : IOpenIddictServerHandler
+ {
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictServerHandlerDescriptor Descriptor { get; }
+ = OpenIddictServerHandlerDescriptor.CreateBuilder()
+ .UseSingletonHandler()
+ .SetOrder(int.MinValue + 100_000)
+ .SetType(OpenIddictServerHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public ValueTask HandleAsync(ApplyTokenResponseContext context)
+ {
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (string.IsNullOrEmpty(context.Error))
+ {
+ return default;
+ }
+
+ // If the error indicates an invalid token caused by an invalid authorization,
+ // device code or refresh token, return a standard invalid_grant.
+
+ if (context.Request is null || !(context.Request.IsAuthorizationCodeGrantType() ||
+ context.Request.IsDeviceCodeGrantType() ||
+ context.Request.IsRefreshTokenGrantType()))
+ {
+ return default;
+ }
+
+
+ context.Response.Error = context.Error switch
+ {
+ Errors.InvalidToken or Errors.ExpiredToken => Errors.InvalidGrant,
+
+ _ => context.Error // Otherwise, keep the error as-is.
+ };
+
+ return default;
+ }
+ }
}
}
}
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs
index 65dbff23..31267684 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs
@@ -184,29 +184,25 @@ namespace OpenIddict.Server
!await _tokenManager.HasTypeAsync(token, context.ValidTokenTypes.ToImmutableArray()))
{
context.Reject(
- error: context.EndpointType switch
+ error: Errors.InvalidToken,
+ description: context.ValidTokenTypes.Count switch
{
- OpenIddictServerEndpointType.Token => Errors.InvalidGrant,
- _ => Errors.InvalidToken
- },
- description: context.EndpointType switch
- {
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.AuthorizationCode)
=> SR.GetResourceString(SR.ID2001),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.DeviceCode)
=> SR.GetResourceString(SR.ID2002),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.RefreshToken)
=> SR.GetResourceString(SR.ID2003),
_ => SR.GetResourceString(SR.ID2004)
},
- uri: context.EndpointType switch
+ uri: context.ValidTokenTypes.Count switch
{
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.AuthorizationCode)
=> SR.FormatID8000(SR.ID2001),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.DeviceCode)
=> SR.FormatID8000(SR.ID2002),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.RefreshToken)
=> SR.FormatID8000(SR.ID2003),
_ => SR.FormatID8000(SR.ID2004),
@@ -305,25 +301,22 @@ namespace OpenIddict.Server
context.Logger.LogTrace(result.Exception, SR.GetResourceString(SR.ID6000), context.Token);
context.Reject(
- error: context.EndpointType switch
- {
- OpenIddictServerEndpointType.Token => Errors.InvalidGrant,
- _ => Errors.InvalidToken
- },
+ error: Errors.InvalidToken,
description: result.Exception switch
{
- SecurityTokenInvalidTypeException => context.EndpointType switch
+ SecurityTokenInvalidTypeException => context.ValidTokenTypes.Count switch
{
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.AuthorizationCode)
=> SR.GetResourceString(SR.ID2005),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.DeviceCode)
=> SR.GetResourceString(SR.ID2006),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.RefreshToken)
=> SR.GetResourceString(SR.ID2007),
- OpenIddictServerEndpointType.Userinfo => SR.GetResourceString(SR.ID2008),
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.AccessToken)
+ => SR.GetResourceString(SR.ID2008),
_ => SR.GetResourceString(SR.ID2089)
},
@@ -336,18 +329,19 @@ namespace OpenIddict.Server
},
uri: result.Exception switch
{
- SecurityTokenInvalidTypeException => context.EndpointType switch
+ SecurityTokenInvalidTypeException => context.ValidTokenTypes.Count switch
{
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.AuthorizationCode)
=> SR.FormatID8000(SR.ID2005),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.DeviceCode)
=> SR.FormatID8000(SR.ID2006),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.RefreshToken)
=> SR.FormatID8000(SR.ID2007),
- OpenIddictServerEndpointType.Userinfo => SR.FormatID8000(SR.ID2008),
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.AccessToken)
+ => SR.FormatID8000(SR.ID2008),
_ => SR.FormatID8000(SR.ID2089)
},
@@ -629,36 +623,30 @@ namespace OpenIddict.Server
if (context.Principal is null)
{
context.Reject(
- error: context.EndpointType switch
+ error: Errors.InvalidToken,
+ description: context.ValidTokenTypes.Count switch
{
- OpenIddictServerEndpointType.Token => Errors.InvalidGrant,
- _ => Errors.InvalidToken
- },
- description: context.EndpointType switch
- {
- OpenIddictServerEndpointType.Authorization or OpenIddictServerEndpointType.Logout
- => SR.GetResourceString(SR.ID2009),
-
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.AuthorizationCode)
=> SR.GetResourceString(SR.ID2001),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.DeviceCode)
=> SR.GetResourceString(SR.ID2002),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.RefreshToken)
=> SR.GetResourceString(SR.ID2003),
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.IdToken)
+ => SR.GetResourceString(SR.ID2009),
_ => SR.GetResourceString(SR.ID2004)
},
- uri: context.EndpointType switch
+ uri: context.ValidTokenTypes.Count switch
{
- OpenIddictServerEndpointType.Authorization or OpenIddictServerEndpointType.Logout
- => SR.FormatID8000(SR.ID2009),
-
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.AuthorizationCode)
=> SR.FormatID8000(SR.ID2001),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.DeviceCode)
=> SR.FormatID8000(SR.ID2002),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.RefreshToken)
=> SR.FormatID8000(SR.ID2003),
+ 1 when context.ValidTokenTypes.Contains(TokenTypeHints.IdToken)
+ => SR.FormatID8000(SR.ID2009),
_ => SR.FormatID8000(SR.ID2004)
});
@@ -735,30 +723,20 @@ namespace OpenIddict.Server
if (token is null)
{
context.Reject(
- error: context.EndpointType switch
+ error: Errors.InvalidToken,
+ description: context.Principal.GetTokenType() switch
{
- OpenIddictServerEndpointType.Token => Errors.InvalidGrant,
- _ => Errors.InvalidToken
- },
- description: context.EndpointType switch
- {
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
- => SR.GetResourceString(SR.ID2001),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
- => SR.GetResourceString(SR.ID2002),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
- => SR.GetResourceString(SR.ID2003),
+ TokenTypeHints.AuthorizationCode => SR.GetResourceString(SR.ID2001),
+ TokenTypeHints.DeviceCode => SR.GetResourceString(SR.ID2002),
+ TokenTypeHints.RefreshToken => SR.GetResourceString(SR.ID2003),
_ => SR.GetResourceString(SR.ID2004)
},
- uri: context.EndpointType switch
+ uri: context.Principal.GetTokenType() switch
{
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
- => SR.FormatID8000(SR.ID2001),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
- => SR.FormatID8000(SR.ID2002),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
- => SR.FormatID8000(SR.ID2003),
+ TokenTypeHints.AuthorizationCode => SR.FormatID8000(SR.ID2001),
+ TokenTypeHints.DeviceCode => SR.FormatID8000(SR.ID2002),
+ TokenTypeHints.RefreshToken => SR.FormatID8000(SR.ID2003),
_ => SR.FormatID8000(SR.ID2004)
});
@@ -766,88 +744,69 @@ namespace OpenIddict.Server
return;
}
- if (context.EndpointType == OpenIddictServerEndpointType.Token && (context.Request.IsAuthorizationCodeGrantType() ||
- context.Request.IsDeviceCodeGrantType() ||
- context.Request.IsRefreshTokenGrantType()))
+ // If the token is already marked as redeemed, this may indicate that it was compromised.
+ // In this case, revoke the entire chain of tokens associated with the authorization.
+ // Special logic is used to avoid revoking refresh tokens already marked as redeemed to allow for a small leeway.
+ // Note: the authorization itself is not revoked to allow the legitimate client to start a new flow.
+ // See https://tools.ietf.org/html/rfc6749#section-10.5 for more information.
+ if (await _tokenManager.HasStatusAsync(token, Statuses.Redeemed))
{
- // If the authorization code/device code/refresh token is already marked as redeemed, this may indicate
- // that it was compromised. In this case, revoke the entire chain of tokens associated with the authorization.
- // Special logic is used to avoid revoking refresh tokens already marked as redeemed to allow for a small leeway.
- // Note: the authorization itself is not revoked to allow the legitimate client to start a new flow.
- // See https://tools.ietf.org/html/rfc6749#section-10.5 for more information.
- if (await _tokenManager.HasStatusAsync(token, Statuses.Redeemed))
+ if (!context.Principal.HasTokenType(TokenTypeHints.RefreshToken) || !await IsReusableAsync(token))
{
- if (!context.Request.IsRefreshTokenGrantType() || !await IsReusableAsync(token))
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6002), identifier);
-
- context.Reject(
- error: context.EndpointType switch
- {
- OpenIddictServerEndpointType.Token => Errors.InvalidGrant,
-
- _ => Errors.InvalidToken
- },
- description: context.EndpointType switch
- {
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
- => SR.GetResourceString(SR.ID2010),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
- => SR.GetResourceString(SR.ID2011),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
- => SR.GetResourceString(SR.ID2012),
-
- _ => SR.GetResourceString(SR.ID2013)
- },
- uri: context.EndpointType switch
- {
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
- => SR.FormatID8000(SR.ID2010),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
- => SR.FormatID8000(SR.ID2011),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
- => SR.FormatID8000(SR.ID2012),
-
- _ => SR.FormatID8000(SR.ID2013)
- });
-
- // Revoke all the token entries associated with the authorization.
- await TryRevokeChainAsync(await _tokenManager.GetAuthorizationIdAsync(token));
-
- return;
- }
+ context.Logger.LogInformation(SR.GetResourceString(SR.ID6002), identifier);
+
+ context.Reject(
+ error: Errors.InvalidToken,
+ description: context.Principal.GetTokenType() switch
+ {
+ TokenTypeHints.AuthorizationCode => SR.GetResourceString(SR.ID2010),
+ TokenTypeHints.DeviceCode => SR.GetResourceString(SR.ID2011),
+ TokenTypeHints.RefreshToken => SR.GetResourceString(SR.ID2012),
+
+ _ => SR.GetResourceString(SR.ID2013)
+ },
+ uri: context.Principal.GetTokenType() switch
+ {
+ TokenTypeHints.AuthorizationCode => SR.FormatID8000(SR.ID2010),
+ TokenTypeHints.DeviceCode => SR.FormatID8000(SR.ID2011),
+ TokenTypeHints.RefreshToken => SR.FormatID8000(SR.ID2012),
+
+ _ => SR.FormatID8000(SR.ID2013)
+ });
+
+ // Revoke all the token entries associated with the authorization.
+ await TryRevokeChainAsync(await _tokenManager.GetAuthorizationIdAsync(token));
return;
}
- if (context.Request.IsDeviceCodeGrantType())
- {
- // If the device code is not marked as valid yet, return an authorization_pending error.
- if (await _tokenManager.HasStatusAsync(token, Statuses.Inactive))
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6003), identifier);
+ return;
+ }
- context.Reject(
- error: Errors.AuthorizationPending,
- description: SR.GetResourceString(SR.ID2014),
- uri: SR.FormatID8000(SR.ID2014));
+ // If the token is not marked as valid yet, return an authorization_pending error.
+ if (await _tokenManager.HasStatusAsync(token, Statuses.Inactive))
+ {
+ context.Logger.LogInformation(SR.GetResourceString(SR.ID6003), identifier);
- return;
- }
+ context.Reject(
+ error: Errors.AuthorizationPending,
+ description: SR.GetResourceString(SR.ID2014),
+ uri: SR.FormatID8000(SR.ID2014));
- // If the device code is marked as rejected, return an access_denied error.
- if (await _tokenManager.HasStatusAsync(token, Statuses.Rejected))
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6004), identifier);
+ return;
+ }
- context.Reject(
- error: Errors.AccessDenied,
- description: SR.GetResourceString(SR.ID2015),
- uri: SR.FormatID8000(SR.ID2015));
+ // If the token is marked as rejected, return an access_denied error.
+ if (await _tokenManager.HasStatusAsync(token, Statuses.Rejected))
+ {
+ context.Logger.LogInformation(SR.GetResourceString(SR.ID6004), identifier);
- return;
- }
- }
+ context.Reject(
+ error: Errors.AccessDenied,
+ description: SR.GetResourceString(SR.ID2015),
+ uri: SR.FormatID8000(SR.ID2015));
+
+ return;
}
if (!await _tokenManager.HasStatusAsync(token, Statuses.Valid))
@@ -855,30 +814,20 @@ namespace OpenIddict.Server
context.Logger.LogInformation(SR.GetResourceString(SR.ID6005), identifier);
context.Reject(
- error: context.EndpointType switch
- {
- OpenIddictServerEndpointType.Token => Errors.InvalidGrant,
- _ => Errors.InvalidToken
- },
- description: context.EndpointType switch
+ error: Errors.InvalidToken,
+ description: context.Principal.GetTokenType() switch
{
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
- => SR.GetResourceString(SR.ID2016),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
- => SR.GetResourceString(SR.ID2017),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
- => SR.GetResourceString(SR.ID2018),
+ TokenTypeHints.AuthorizationCode => SR.GetResourceString(SR.ID2016),
+ TokenTypeHints.DeviceCode => SR.GetResourceString(SR.ID2017),
+ TokenTypeHints.RefreshToken => SR.GetResourceString(SR.ID2018),
_ => SR.GetResourceString(SR.ID2019)
},
- uri: context.EndpointType switch
+ uri: context.Principal.GetTokenType() switch
{
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
- => SR.FormatID8000(SR.ID2016),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
- => SR.FormatID8000(SR.ID2017),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
- => SR.FormatID8000(SR.ID2018),
+ TokenTypeHints.AuthorizationCode => SR.FormatID8000(SR.ID2016),
+ TokenTypeHints.DeviceCode => SR.FormatID8000(SR.ID2017),
+ TokenTypeHints.RefreshToken => SR.FormatID8000(SR.ID2018),
_ => SR.FormatID8000(SR.ID2019)
});
@@ -975,30 +924,20 @@ namespace OpenIddict.Server
context.Logger.LogInformation(SR.GetResourceString(SR.ID6006), identifier);
context.Reject(
- error: context.EndpointType switch
- {
- OpenIddictServerEndpointType.Token => Errors.InvalidGrant,
- _ => Errors.InvalidToken
- },
- description: context.EndpointType switch
+ error: Errors.InvalidToken,
+ description: context.Principal.GetTokenType() switch
{
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
- => SR.GetResourceString(SR.ID2020),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
- => SR.GetResourceString(SR.ID2021),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
- => SR.GetResourceString(SR.ID2022),
+ TokenTypeHints.AuthorizationCode => SR.GetResourceString(SR.ID2020),
+ TokenTypeHints.DeviceCode => SR.GetResourceString(SR.ID2021),
+ TokenTypeHints.RefreshToken => SR.GetResourceString(SR.ID2022),
_ => SR.GetResourceString(SR.ID2023)
},
- uri: context.EndpointType switch
+ uri: context.Principal.GetTokenType() switch
{
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
- => SR.FormatID8000(SR.ID2020),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
- => SR.FormatID8000(SR.ID2021),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
- => SR.FormatID8000(SR.ID2022),
+ TokenTypeHints.AuthorizationCode => SR.FormatID8000(SR.ID2020),
+ TokenTypeHints.DeviceCode => SR.FormatID8000(SR.ID2021),
+ TokenTypeHints.RefreshToken => SR.FormatID8000(SR.ID2022),
_ => SR.FormatID8000(SR.ID2023)
});
@@ -1033,9 +972,7 @@ namespace OpenIddict.Server
Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006));
- // Don't validate the lifetime of id_tokens used as id_token_hints.
- if (context.ValidTokenTypes.Count is 1 && context.ValidTokenTypes.ElementAt(0) is TokenTypeHints.IdToken &&
- context.EndpointType is OpenIddictServerEndpointType.Authorization or OpenIddictServerEndpointType.Logout)
+ if (context.DisableLifetimeValidation)
{
return default;
}
@@ -1044,34 +981,24 @@ namespace OpenIddict.Server
if (date.HasValue && date.Value < DateTimeOffset.UtcNow)
{
context.Reject(
- error: context.EndpointType switch
+ error: context.Principal.GetTokenType() switch
{
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
- => Errors.ExpiredToken,
-
- OpenIddictServerEndpointType.Token => Errors.InvalidGrant,
-
- _ => Errors.InvalidToken
+ TokenTypeHints.DeviceCode => Errors.ExpiredToken,
+ _ => Errors.InvalidToken
},
- description: context.EndpointType switch
+ description: context.Principal.GetTokenType() switch
{
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
- => SR.GetResourceString(SR.ID2016),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
- => SR.GetResourceString(SR.ID2017),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
- => SR.GetResourceString(SR.ID2018),
+ TokenTypeHints.AuthorizationCode => SR.GetResourceString(SR.ID2016),
+ TokenTypeHints.DeviceCode => SR.GetResourceString(SR.ID2017),
+ TokenTypeHints.RefreshToken => SR.GetResourceString(SR.ID2018),
_ => SR.GetResourceString(SR.ID2019)
},
- uri: context.EndpointType switch
+ uri: context.Principal.GetTokenType() switch
{
- OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
- => SR.FormatID8000(SR.ID2016),
- OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
- => SR.FormatID8000(SR.ID2017),
- OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
- => SR.FormatID8000(SR.ID2018),
+ TokenTypeHints.AuthorizationCode => SR.FormatID8000(SR.ID2016),
+ TokenTypeHints.DeviceCode => SR.FormatID8000(SR.ID2017),
+ TokenTypeHints.RefreshToken => SR.FormatID8000(SR.ID2018),
_ => SR.FormatID8000(SR.ID2019)
});
@@ -1170,6 +1097,11 @@ namespace OpenIddict.Server
throw new ArgumentNullException(nameof(context));
}
+ if (!context.CreateTokenEntry)
+ {
+ return;
+ }
+
var descriptor = new OpenIddictTokenDescriptor
{
AuthorizationId = context.Principal.GetAuthorizationId(),
@@ -1201,9 +1133,9 @@ namespace OpenIddict.Server
};
// If the client application is known, associate it with the token.
- if (!string.IsNullOrEmpty(context.Request.ClientId))
+ if (!string.IsNullOrEmpty(context.ClientId))
{
- var application = await _applicationManager.FindByClientIdAsync(context.Request.ClientId);
+ var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
if (application is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0017));
@@ -1386,25 +1318,7 @@ namespace OpenIddict.Server
throw new ArgumentNullException(nameof(context));
}
- if (!(context.TokenType switch
- {
- // Access and refresh tokens can be converted to reference tokens
- // if the corresponding option was enabled in the server options.
- TokenTypeHints.AccessToken => context.Options.UseReferenceAccessTokens,
- TokenTypeHints.RefreshToken => context.Options.UseReferenceRefreshTokens,
-
- // By default, authorization/user codes are always converted to reference tokens.
- TokenTypeHints.AuthorizationCode or TokenTypeHints.UserCode => true,
-
- // Device codes are only converted to reference tokens if they are not generated
- // as part of a device code swap made by the user code verification endpoint.
- TokenTypeHints.DeviceCode => context.EndpointType is not OpenIddictServerEndpointType.Verification,
-
- // Identity tokens cannot be converted to reference tokens.
- TokenTypeHints.IdToken => false,
-
- _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0003))
- }))
+ if (!context.PersistTokenPayload)
{
return;
}
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs
index 6b425d35..2b9fa38a 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs
@@ -695,6 +695,9 @@ namespace OpenIddict.Server
var notification = new ValidateTokenContext(context.Transaction)
{
+ // Don't validate the lifetime of id_tokens used as id_token_hints.
+ DisableLifetimeValidation = context.EndpointType is OpenIddictServerEndpointType.Authorization or
+ OpenIddictServerEndpointType.Logout,
Token = context.IdentityToken,
ValidTokenTypes = { TokenTypeHints.IdToken }
};
@@ -2218,6 +2221,11 @@ namespace OpenIddict.Server
var notification = new GenerateTokenContext(context.Transaction)
{
+ ClientId = context.ClientId,
+ CreateTokenEntry = !context.Options.DisableTokenStorage,
+ // Access tokens can be converted to reference tokens if the
+ // corresponding option was enabled in the server options.
+ PersistTokenPayload = context.Options.UseReferenceAccessTokens,
Principal = context.AccessTokenPrincipal!,
TokenType = TokenTypeHints.AccessToken
};
@@ -2280,6 +2288,9 @@ namespace OpenIddict.Server
var notification = new GenerateTokenContext(context.Transaction)
{
+ ClientId = context.ClientId,
+ CreateTokenEntry = !context.Options.DisableTokenStorage,
+ PersistTokenPayload = !context.Options.DisableTokenStorage,
Principal = context.AuthorizationCodePrincipal!,
TokenType = TokenTypeHints.AuthorizationCode
};
@@ -2342,6 +2353,16 @@ namespace OpenIddict.Server
var notification = new GenerateTokenContext(context.Transaction)
{
+ ClientId = context.ClientId,
+ CreateTokenEntry = !context.Options.DisableTokenStorage,
+ // Device codes can be converted to reference tokens if they are not generated
+ // as part of a device code swap made by the user code verification endpoint.
+ PersistTokenPayload = context.EndpointType switch
+ {
+ OpenIddictServerEndpointType.Verification => false,
+
+ _ => !context.Options.DisableTokenStorage
+ },
Principal = context.DeviceCodePrincipal!,
TokenType = TokenTypeHints.DeviceCode
};
@@ -2404,6 +2425,11 @@ namespace OpenIddict.Server
var notification = new GenerateTokenContext(context.Transaction)
{
+ ClientId = context.ClientId,
+ CreateTokenEntry = !context.Options.DisableTokenStorage,
+ // Refresh tokens can be converted to reference tokens if the
+ // corresponding option was enabled in the server options.
+ PersistTokenPayload = context.Options.UseReferenceRefreshTokens,
Principal = context.RefreshTokenPrincipal!,
TokenType = TokenTypeHints.RefreshToken
};
@@ -2711,6 +2737,9 @@ namespace OpenIddict.Server
var notification = new GenerateTokenContext(context.Transaction)
{
+ ClientId = context.ClientId,
+ CreateTokenEntry = !context.Options.DisableTokenStorage,
+ PersistTokenPayload = !context.Options.DisableTokenStorage,
Principal = context.UserCodePrincipal!,
TokenType = TokenTypeHints.UserCode
};
@@ -2773,6 +2802,10 @@ namespace OpenIddict.Server
var notification = new GenerateTokenContext(context.Transaction)
{
+ ClientId = context.ClientId,
+ CreateTokenEntry = !context.Options.DisableTokenStorage,
+ // Identity tokens cannot never be reference tokens.
+ PersistTokenPayload = false,
Principal = context.IdentityTokenPrincipal!,
TokenType = TokenTypeHints.IdToken
};
diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs
index b0d82312..bdc56f97 100644
--- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs
+++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs
@@ -3436,6 +3436,9 @@ namespace OpenIddict.Server.IntegrationTests
mock.Setup(manager => manager.GetIdAsync(token, It.IsAny()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+ mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny()))
+ .ReturnsAsync(TokenTypeHints.AuthorizationCode);
+
mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny()))
.ReturnsAsync(false);
@@ -3535,6 +3538,9 @@ namespace OpenIddict.Server.IntegrationTests
mock.Setup(manager => manager.GetIdAsync(token, It.IsAny()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+ mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny()))
+ .ReturnsAsync(TokenTypeHints.AuthorizationCode);
+
mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny()))
.ReturnsAsync(false);
@@ -3618,6 +3624,9 @@ namespace OpenIddict.Server.IntegrationTests
mock.Setup(manager => manager.GetIdAsync(token, It.IsAny()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+ mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny()))
+ .ReturnsAsync(TokenTypeHints.RefreshToken);
+
mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny()))
.ReturnsAsync(false);
@@ -3703,6 +3712,9 @@ namespace OpenIddict.Server.IntegrationTests
mock.Setup(manager => manager.GetIdAsync(token, It.IsAny()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+ mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny()))
+ .ReturnsAsync(TokenTypeHints.RefreshToken);
+
mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny()))
.ReturnsAsync(false);
@@ -3775,18 +3787,13 @@ namespace OpenIddict.Server.IntegrationTests
builder.UseInlineHandler(context =>
{
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
- .SetTokenType(context.Request.IsAuthorizationCodeGrantType() ?
+ .SetTokenType(context.Request!.IsAuthorizationCodeGrantType() ?
TokenTypeHints.AuthorizationCode :
TokenTypeHints.RefreshToken)
.SetPresenters("Fabrikam")
.SetTokenId("0270F515-C5B1-4FBF-B673-D7CAF7CCDABC")
.SetClaim(Claims.Subject, "Bob le Bricoleur");
- if (context.Request.IsAuthorizationCodeGrantType())
- {
- context.Principal.SetPresenters("Fabrikam");
- }
-
return default;
});
diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs
index 1c0cebf4..48fd0628 100644
--- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs
+++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs
@@ -221,8 +221,8 @@ namespace OpenIddict.Server.IntegrationTests
// Assert
Assert.Equal(Errors.InvalidToken, response.Error);
- Assert.Equal(SR.GetResourceString(SR.ID2019), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2019), response.ErrorUri);
+ Assert.Equal(SR.GetResourceString(SR.ID2018), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2018), response.ErrorUri);
}
[Theory]
diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs
index 95560956..00b3c483 100644
--- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs
+++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs
@@ -705,8 +705,8 @@ namespace OpenIddict.Server.IntegrationTests
// Assert
Assert.Equal(Errors.InvalidToken, response.Error);
- Assert.Equal(SR.GetResourceString(SR.ID2004), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2004), response.ErrorUri);
+ Assert.Equal(SR.GetResourceString(SR.ID2003), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2003), response.ErrorUri);
Mock.Get(manager).Verify(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.TryRevokeAsync(It.IsAny(), It.IsAny()), Times.Never());
@@ -766,8 +766,8 @@ namespace OpenIddict.Server.IntegrationTests
// Assert
Assert.Equal(Errors.InvalidToken, response.Error);
- Assert.Equal(SR.GetResourceString(SR.ID2019), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2019), response.ErrorUri);
+ Assert.Equal(SR.GetResourceString(SR.ID2018), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2018), response.ErrorUri);
Mock.Get(manager).Verify(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.TryRevokeAsync(It.IsAny(), It.IsAny()), Times.Never());