IdentityServer4 - 如何从另一个 ApiResource 调用一个 ApiResource

Posted

技术标签:

【中文标题】IdentityServer4 - 如何从另一个 ApiResource 调用一个 ApiResource【英文标题】:IdentityServer4 - How to Call One ApiResource from Another ApiResource 【发布时间】:2019-01-07 07:23:02 【问题描述】:

我有一个 spa 应用程序设置,它使用带有 OpenId Connect 的混合授权流程受 IdentityServer4 保护。

这个 spa 应用程序能够调用多个 ApiResources,作为登录用户,通过请求 access_token

HttpContextAccessor.HttpContext.GetTokenAsync("access_token")

这对于多个 ApiResources 非常有用。例如,我的 Spa 应用程序可以毫无问题地调用 ApiResource1、ApiResource2 和 ApiResource3。

但是,我添加了一个新的 ApiResource(即 ApiResource4),我需要能够从 ApiResource1 调用它。但是,当我尝试调用

HttpContextAccessor.HttpContext.GetTokenAsync("access_token")

来自 ApiResource1,access_token 为 null,因此调用失败。

如何使用已登录的用户令牌从 ApiResource1 调用 ApiResource2?

也许更好的提问方式是,如何从 ApiResource1 获取登录用户 access_token?

我的 SPA 应用程序是这样设置的:

services.AddAuthentication(options =>
                
                    options.DefaultScheme = "Cookies";
                    options.DefaultChallengeScheme = "oidc";
                )
                .AddCookie("Cookies", options =>
                
                    options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
                )
                .AddOpenIdConnect("oidc", options =>
                
                    options.SignInScheme = "Cookies";

                    options.Authority = Configuration.GetSection("IdentityServerOptions").GetValue<string>("BaseUrl");
                    options.RequireHttpsMetadata = true;

                    options.ClientId = Configuration.GetSection("IdentityServerOptions").GetValue<string>("ClientId");
                    options.ClientSecret = Configuration.GetSection("IdentityServerOptions").GetValue<string>("ClientSecret");

                    options.SaveTokens = true;
                    options.GetClaimsFromUserInfoEndpoint = true;

                    options.Scope.Add("ApiResource1");
                    options.Scope.Add("ApiResource2");
                    options.Scope.Add("ApiResource3");
                    options.Scope.Add("ApiResource4");
                    options.ResponseType = "code id_token";
                );

我的 ApiResources 设置如下:

services.AddAuthentication("Bearer")
                .AddIdentityServerAuthentication(options =>
                
                    options.Authority = Configuration.GetSection("IdentityServer").GetValue<string>("BaseUrl");
                    options.RequireHttpsMetadata = true;
                    options.ApiName = _AppName;
                );

【问题讨论】:

查看扩展授权委托docs.identityserver.io/en/release/topics/…。这听起来像你所追求的。另一种选择是转发当前用户access_token,方法是从请求授权标头中提取它。 这听起来正是我想要的。我明天将尝试实施它并确保它有效。谢谢! @Brad 如何获得您链接的文档中提到的“userToken”?我已经尝试了以下并且全部返回 null: string userToken = _contextAccessor.HttpContext != null ?等待 _contextAccessor.HttpContext?.GetTokenAsync("id_token") ??等待 _contextAccessor.HttpContext?.GetTokenAsync("token") ??等待_contextAccessor.HttpContext?.GetTokenAsync("access_token") GetTokenAsync() 仅适用于 cookie 身份验证,不适用于使用 JWT 的 API。令牌位于 Authorization 标头中。它应该在_context.HttpContext.Request.Headers 谢谢布拉德。我没有意识到 GetTokenAsync 仅适用于 cookie 身份验证。我最终确实从 Auth 标头中手动检索了令牌,并且结合您的第一条评论解决了这个问题。谢谢!如果您提交答案,我会接受。 【参考方案1】:

HttpContext.GetTokenAsync("access_token") 扩展方法仅适用于 Cookie 身份验证。它将尝试提取存储在 cookie 中的令牌。当您拥有使用 JWT 身份验证的 API 时,此功能不可用。

你有两个选择。

    使用扩展授权委托,如 Identity Server 文档 http://docs.identityserver.io/en/release/topics/extension_grants.html#example-simple-delegation-using-an-extension-grant 中所述。此选项实施起来更复杂,但它提供了更高的安全性。

    Authorization 标头中提取并转发 JWT。这个选项就像从 Request 中获取 header 并在发出 API 请求之前将其设置在 HttpClient 的 header 中一样简单。

【讨论】:

以上是关于IdentityServer4 - 如何从另一个 ApiResource 调用一个 ApiResource的主要内容,如果未能解决你的问题,请参考以下文章

如何将 IdentityServer4 设置为外部身份提供者

IdentityServer4如何创建身份令牌?

如何测试我的令牌是不是使用 IdentityServer4 刷新?

IdentityServer4 如何在授权代码流中存储和更新令牌

如何在 IdentityServer4 中创建自定义授权类型

如何加固 IdentityServer4 以用于生产?