How to override refresh_token logic?
Confirm you've already contributed to this project or that you sponsor it
- [x] I confirm I'm a sponsor or a contributor
Version
6.2.0
Question
Hi! I want to override refresh_token logic for migration purposes. I need to apply custom logic for refreshing token (validating and constructing principal) if default implementation rejected. How to properly implement this?
I am using
IOpenIddictServerHandler<OpenIddictServerEvents.HandleTokenRequestContext>
but it is not firing if default validation rejects the request.
Hi,
How to properly implement this?
It's a bit unclear what your requirements. Care to elaborate what you're trying to implement exactly?
I have identityServer4 identity provider with the database and a lot of refresh_tokens. I need to migrate from old identityprovider(is4) to new one (openiddict). I want clients to migrate smoothly (simply to change authority url).
Then if client make refresh_token request I want to validate it either on NEW indetity or if failed on OLD one. I want client to fail on refresh only if refresh token is invalid on BOTH identities.
Ah, in this case you'll want to implement that at the ValidateToken(Context) level, which is the event responsible for extracting token payloads and validating them.
Here's an example that produces an arbitrary "refresh token principal" for user [email protected] when the token is the hardcoded magic_token string (in your case, you'll want to query IdSrv's database and populate the principal based on that, obviously 😄)
/// <summary>
/// Contains the logic responsible for processing custom refresh tokens.
/// </summary>
public sealed class ValidateCustomRefreshToken : IOpenIddictServerHandler<OpenIddictServerEvents.ValidateTokenContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<OpenIddictServerEvents.ValidateTokenContext>()
.UseSingletonHandler<ValidateCustomRefreshToken>()
.SetOrder(OpenIddictServerHandlers.Protection.ValidatePrincipal.Descriptor.Order - 500)
.SetType(OpenIddictServerHandlerType.Custom)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(OpenIddictServerEvents.ValidateTokenContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// A null principal at this point indicates that the OpenIddict server was unable
// to handle the token (e.g because the format wasn't recognized/isn't supported or
// because it's malformed). In that case, you still have a chance to populate the
// principal yourself, for instance to implement and support custom token formats.
if (context.Principal is not null)
{
return default;
}
if (context.Token is "magic_token")
{
var identity = new ClaimsIdentity(TokenValidationParameters.DefaultAuthenticationType);
identity.SetClaim(Claims.Subject, "[email protected]");
// Note: you MUST be 100% sure the processed token is a refresh token before
// calling this method, as it's used by OpenIddict to validate the token type.
identity.SetTokenType(TokenTypeHints.RefreshToken);
// Note: if you're using the OpenIddict 7.0 previews, the new URI-style token types
// MUST be used instead of the token_type_hint constants used in previous versions.
//
// See https://github.com/openiddict/openiddict-core/issues/2296 for more information.
// identity.SetTokenType(TokenTypeIdentifiers.RefreshToken);
context.Principal = new ClaimsPrincipal(identity);
}
return default;
}
}
services.AddOpenIddict()
.AddServer(options =>
{
options.AddEventHandler(ValidateCustomRefreshToken.Descriptor);
});
Hope that'll help.
Thanks. It returns access_token but not refresh_token. How to fix this? I need new refresh_token to be created and returned.
How to fix this? I need new refresh_token to be created and returned.
Make sure the offline_access scope is granted (e.g identity.SetScopes(Scopes.OfflineAccess)).
Did that work, @kaputsyn? 😃