We are using the OpenIddict degraded mode to wrap around another login type. This works. However at first I didn't get the refresh token to work. I didn't find any example where the refresh token was implemented in combination with the degraded mode. We found a way to make it work, but it feels hacky. The part about the refresh flow is below:
options.AddEventHandler<ValidateTokenContext>(builder =>
builder.UseInlineHandler(async context =>
{
if (context.Request.IsRefreshTokenGrantType())
{
//Fetching the user principal based on the provided refresh token
var req = new GetRefreshTokenRequest { RefreshToken = context.Request.RefreshToken, ClientID = context.Request.ClientId };
var newRefreshToken = await _mediator.Send(req);
if (newRefreshToken != null)
{
var user = await _mediator.Send(new GetUserByEmailRequest { Email = newRefreshToken.Email });
//We have to provide a principal, if it was a valid refresh token
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.RefreshToken)
.SetClaim(Claims.Subject, user.Email);
//A bit hacky, but we pass through the refresh token
//This way HandleTokenRequestContext won't need to fetch it again
context.Request.RefreshToken = newRefreshToken.Token;
return;
}
else
{
context.Reject(
error: Errors.InvalidRequest,
description: "The provided refreshtoken is not valid."
);
return;
}
}
return;
}).SetOrder(Protection.ValidateIdentityModelToken.Descriptor.Order - 500)
);
options.AddEventHandler<HandleTokenRequestContext>(builder =>
builder.UseInlineHandler(async context =>
{
var identity = new ClaimsIdentity(TokenValidationParameters.DefaultAuthenticationType);
identity.AddClaim(OpenIddictConstants.Claims.Subject, context.ClientId);
if (context.Request.IsRefreshTokenGrantType())
{
// Retrieve the claims principal stored in the refresh token.
//ValidateTokenContext already fetched a new refreshtoken and stored it in the request, we now add it to the response
context.Parameters[OpenIddictServerAspNetCoreConstants.Tokens.RefreshToken] = context.Request.RefreshToken;
}
else
{
//get first refresh token
var firstRefreshToken = new PBKDF2().GenerateSalt(1, 32);
var email = context.Principal.GetClaim(ClaimTypes.Email);
await _mediator.Send(new SaveRefreshTokenRequest { Email = email, RefreshToken = firstRefreshToken, ClientId = context.ClientId });
context.Parameters[OpenIddictServerAspNetCoreConstants.Tokens.RefreshToken] = firstRefreshToken;
}
var cp = new ClaimsPrincipal(identity);
cp.SetScopes(context.Request.GetScopes());
context.SignIn(context.Principal);
return;
}));
I upgraded to OpenIddict 4.0.0-preview3, to be able to use "context.Parameters". Is there a way to make this work with 3.0? Is there a more standard way to make the refresh work in degraded mode?