问题描述
我正在关注这个关于允许 OpenIDDict 包装替代身份验证提供程序但从 OpenIDDict 本身返回 JWT 令牌的博客:
这实际上是关于拦截授权代码流而不是客户端凭据流,但它提供了一个很好的起点。
不幸的是,它指出“我们不需要覆盖 HandletokenRequestContext”,这适用于博客,但不适用于(据我所知)我的用例。
我想我需要实现一个自定义的 HandletokenRequestContext,但是当我这样做时,代码运行,没有错误,但 HTTP 响应为空。没有生成令牌。
我应该如何正确拦截客户端凭据流,以便我可以调用另一个提供程序来验证凭据、获取结果并将其包含在我需要添加到 JWT 的自定义声明中?
代码如下:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DbContext>(options =>
{
// Configure the context to use an in-memory store - probably not needed?
options.UseInMemoryDatabase(nameof(DbContext));
// Register the entity sets needed by OpenIddict.
options.USEOpenIddict();
});
services.AddOpenIddict()
.AddCore(options =>
{
options.UseEntityFrameworkCore()
.UseDbContext<DbContext>();
})
.AddServer(options =>
{
options.SetTokenEndpointUris("/connect/token");
options
//.AllowRefreshTokenFlow()
.AllowClientCredentialsFlow();
// Register the signing and encryption credentials.
// options.AddDevelopmentEncryptionCertificate()
// .AddDevelopmentSigningCertificate();
//Development only
options
.AddEphemeralEncryptionKey()
.AddEphemeralSigningKey()
.disableAccesstokenEncryption();
// Register scopes (i.e. the modes we can operate in - there may be a better way to do this (different endpoints?)
options.RegisterScopes("normal","registration");
//Todo: Include Quartz for cleaning up old tokens
options.UseAspNetCore()
.EnabletokenEndpointPassthrough();
options.EnableDegradedMode(); //Activates our custom handlers as the only authentication mechansim,otherwise the workflow attempt to invoke our handler *after* the default ones have already Failed
//the request
options.AddEventHandler<ValidatetokenRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
//Todo: Check that the client Id is kNown
if (!string.Equals(context.ClientId,"client-1",StringComparison.Ordinal))
{
context.Reject(
error: Errors.InvalidClient,description: "The specified 'client_id' doesn't match a kNown Client ID.");
return default;
}
return default;
}));
options.AddEventHandler<HandletokenRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
var identity = new ClaimsIdentity(TokenValidationParameters.DefaultAuthenticationType,OpenIddictConstants.Claims.Name,OpenIddictConstants.Claims.Role);
identity.AddClaim(OpenIddictConstants.Claims.Subject,context.ClientId,OpenIddictConstants.Destinations.Accesstoken,OpenIddictConstants.Destinations.IdentityToken);
if (context.Request.Scope == "registration")
{
//Todo: Authenticate against BackOffice system to get it's token so we can add it as a claim
identity.AddClaim("backoffice_token",Guid.NewGuid().ToString(),OpenIddictConstants.Destinations.Accesstoken);
}
else
{
//Todo: Authenticate against internal authentication database as normal
}
var cp = new ClaimsPrincipal(identity);
cp.SetScopes(context.Request.GetScopes());
context.Principal = cp;
//This doesn't work either
//context.SignIn(context.Principal);
//ERROR: When this exits the response is empty
return default;
}));
});
//.AddValidation(options =>
//{
// options.UseLocalServer();
// options.UseAspNetCore();
//});
services.AddControllers();
services.AddHostedService<CredentialLoader>();
}
解决方法
最后,我做了 3 处更改:
在 HandleTokenRequestContext 中:
var identity = new ClaimsIdentity(TokenValidationParameters.DefaultAuthenticationType);
不确定这是否必要。然后删除
context.Principal = cp;
但重新添加
context.SignIn(context.Principal);