问题描述
在IdentityServer4中,您可以指定extension grand为用户启用委派的访问令牌,因此,如果某个Web服务需要在用户请求期间调用另一个Web服务,则可以使用以下命令从IdentityServer请求一个新的访问令牌:有效范围和受众。我的问题是,该赠款被认为是“代表交互式用户”使用的,这导致了我的问题:
如果我的客户端调用了一个在请求过程中需要调用另一个Web服务的Web服务,我如何创建委托访问令牌?
我试图从文档中修改示例,但是扩展名grand期望来自持有“ sub”声明的用户的令牌,而不是来自没有“ sub”声明的客户端的令牌。
>该客户端是一个守护程序应用程序,因此它可以完全自动化运行,并且无需任何用户交互,并且可以通过客户端凭据流进行身份验证。
澄清我的意思:
- 用户访问UI
- UI将用户重定向到身份服务器以进行身份验证(使用授权码流+ PCKE)。 UI返回访问令牌
- 用户界面使用访问令牌调用WebApi A
- WebApi A需要访问WebApi B,但是访问令牌是为WebApi A设计的。因此,WebApi A向IdentityServer要求IdentityServer提供委派的访问令牌以访问WebApi B。
- IdentityServer提供委托的访问令牌。
- 新的访问令牌将传递到WebApi B。
在这里,我们看到了相同的用例,但是没有用户存在,并且守护程序执行了相同的操作:
- deamon应用程序通过客户端Credentails Flow针对IdentityServer进行身份验证,并取回访问令牌
- deamon应用程序使用访问令牌调用WebApi A
- WebApi A需要访问WebApi B,但是访问令牌是为WebApi A设计的。因此,WebApi A向IdentityServer要求IdentityServer提供委派的访问令牌以访问WebApi B。
- 如何使IdentityServer为客户端提供委托访问令牌?
- 新的访问令牌将传递到WebApi B。
解决方法
对于机器对机器(服务到服务)通信,通常使用客户端证书授予。即使没有用户,这也可以使服务进行通信。用户ID(主题)通常包含在使用此流进行保护的API调用中。
查看此article
WebApi-A和WebApi-B之间的通信可以使用客户端凭据流完成,在这里您不需要传递用户的任何访问令牌。取而代之的是,您在A和B之间的API调用中传递了subjectId(userID)和可选的一些其他声明。这种方式更加简单,好处是AB可以在没有任何用户参与的情况下进行通信(例如,在背景)。
,我找到了解决我问题的方法。您可以扩展给定委托大实例的给定示例实现,并以某种方式扩展它,即为客户端颁发委托令牌:
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
var oldToken = context.Request.Raw.Get("token");
if (string.IsNullOrEmpty(oldToken))
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
var result = await _validator.ValidateAccessTokenAsync(oldToken);
if (result.IsError)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
var sub = result.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
if (sub != null)
{
// The old token had a user context
context.Result = new GrantValidationResult(sub,GrantType);
}
// The "magic" is the part below
else
{
// The old token had a client context
var client = result.Claims.FirstOrDefault(c => c.Type.Equals("client_id",StringComparison.Ordinal));
if (string.IsNullOrWhiteSpace(client?.Value))
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidClient);
return;
}
context.Request.ClientId = client.Value;
context.Result = new GrantValidationResult(new Dictionary<string,object>());
}
}