IdentityServer4-客户端的委托访问令牌

问题描述

在IdentityServer4中,您可以指定extension grand用户启用委派的访问令牌,因此,如果某个Web服务需要在用户请求期间调用一个Web服务,则可以使用以下命令从IdentityServer请求一个新的访问令牌:有效范围和受众。我的问题是,该赠款被​​认为是“代表交互式用户”使用的,这导致了我的问题:

如果我的客户端调用一个在请求过程中需要调用一个Web服务的Web服务,我如何创建委托访问令牌?

我试图从文档中修改示例,但是扩展名grand期望来自持有“ sub”声明的用户的令牌,而不是来自没有“ sub”声明的客户端的令牌。

>

该客户端是一个守护程序应用程序,因此它可以完全自动化运行,并且无需任何用户交互,并且可以通过客户端凭据流进行身份验证。

澄清我的意思:

如果用户存在,我们在这里看到一个用例:

Flow if a user is present

  1. 用户访问UI
  2. UI将用户重定向到身份服务器以进行身份​​验证(使用授权码流+ PCKE)。 UI返回访问令牌
  3. 用户界面使用访问令牌调用We​​bApi A
  4. WebApi A需要访问WebApi B,但是访问令牌是为WebApi A设计的。因此,WebApi A向IdentityServer要求IdentityServer提供委派的访问令牌以访问WebApi B。
  5. IdentityServer提供委托的访问令牌。
  6. 新的访问令牌将传递到WebApi B。

在这里,我们看到了相同的用例,但是没有用户存在,并且守护程序执行了相同的操作:

Flow if no user is present

  1. deamon应用程序通过客户端Credentails Flow针对IdentityServer进行身份验证,并取回访问令牌
  2. deamon应用程序使用访问令牌调用We​​bApi A
  3. WebApi A需要访问WebApi B,但是访问令牌是为WebApi A设计的。因此,WebApi A向IdentityServer要求IdentityServer提供委派的访问令牌以访问WebApi B。
  4. 如何使IdentityServer为客户端提供委托访问令牌?
  5. 新的访问令牌将传递到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>());
            }
        }