【问题标题】:Get UserInfo from Access Token - "Forbidden"从访问令牌获取用户信息 - “禁止”
【发布时间】:2019-06-06 08:14:15
【问题描述】:

我想获取访问令牌的声明,但在尝试获取 UserInfo 时,响应返回错误“禁止访问”。 为什么会这样,我该如何解决? 用户信息端点是https://localhost:44307/connect/userinfo 下面的代码将在它工作后被重构。字段 response1 包含错误消息;

    var client = new HttpClient();
    var disco = await client.GetDiscoveryDocumentAsync(Settings.AuthorityUrl);
    if (disco.IsError)
    {
        throw new Exception(disco.Error);
    }
    var tokenRequest = new ClientCredentialsTokenRequest
    {
        Address = Settings.AuthorityUrl + "connect/token",
        ClientId = Settings.ClientId,
        ClientSecret = "secret",
        Scope = "SIR"
    };

    var response = await client.RequestClientCredentialsTokenAsync(tokenRequest);
    var token = response.AccessToken;
    var response1 = await client.GetUserInfoAsync(new UserInfoRequest
    {
        Address = disco.UserInfoEndpoint,
        Token = token
    });

    if (response1.IsError) throw new Exception(response1.Error);

    var claims = response1.Claims;

在我的 IDP 中,我的配置文件是

using IdentityServer4;
using IdentityServer4.Models;
using IdentityServer4.Test;
using System.Collections.Generic;
using System.Security.Claims;

namespace QuickstartIdentityServer
{
    public class Config
    {
        // scopes define the resources in your system
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
                new IdentityResources.Address()
            };
        }

        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("SIR", "Service Inspection Report")
            };
        }

        // clients want to access resources (aka scopes)
        public static IEnumerable<Client> GetClients()
        {
            var baseUri = "http://localhost:53200/";
            // client credentials client
            return new List<Client>
            {

                // OpenID Connect hybrid flow and client credentials client (MVC)
                new Client
                {
                    ClientId = "SIR",
                    ClientName = "SIR",
                    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },

                    RedirectUris = { $"{baseUri}signin-oidc" },
                    PostLogoutRedirectUris = { $"{baseUri}signout-callback-oidc" },

                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        IdentityServerConstants.StandardScopes.Address,
                        "SIR"
                    },
                    AllowOfflineAccess = true,
                    AlwaysIncludeUserClaimsInIdToken = true
                }
            };
        }

        public static List<TestUser> GetUsers()
        {
            return new List<TestUser>
            {
                new TestUser
                {
                    SubjectId = "1",
                    Username = "alice",
                    Password = "password",

                    Claims = new List<Claim>
                    {
                        new Claim("name", "Alice"),
                        new Claim("website", "https://alice.com"),
                        new Claim("address", "1a The Street")
                    }
                },
                new TestUser
                {
                    SubjectId = "2",
                    Username = "bob",
                    Password = "password",

                    Claims = new List<Claim>
                    {
                        new Claim("name", "Bob"),
                        new Claim("website", "https://bob.com"),
                        new Claim("address", "2a The Street")
                    }
                }
            };
        }
    }
}

启动是;

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        // configure identity server with in-memory stores, keys, clients and scopes
        services.AddIdentityServer()
            .AddSigningCredential(new X509Certificate2(Settings.CertPath, Settings.Password))
            .AddInMemoryIdentityResources(Config.GetIdentityResources())
            .AddInMemoryApiResources(Config.GetApiResources())
            .AddInMemoryClients(Config.GetClients())
            .AddTestUsers(Config.GetUsers());

        services.AddAuthentication()
            .AddGoogle("Google", options =>
            {
                options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

                // register your IdentityServer with Google at https://console.developers.google.com
                // enable the Google+ API
                // set the redirect URI to http://localhost:port/signin-google
                options.ClientId = "copy client ID from Google here";
                options.ClientSecret = "copy client secret from Google here";
            })
            .AddOpenIdConnect("oidc", "OpenID Connect", options =>
            {
                options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
                options.SignOutScheme = IdentityServerConstants.SignoutScheme;

                options.Authority = "https://demo.identityserver.io/";
                options.ClientId = "implicit";

                options.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",
                    RoleClaimType = "role"
                };
            });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole();
        loggerFactory.AddDebug();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseMiddleware<StackifyMiddleware.RequestTracerMiddleware>();
        app.UseIdentityServer();
        app.UseStaticFiles();
        app.UseMvcWithDefaultRoute();
    }
}

【问题讨论】:

  • 您能否提供更多有关该错误的详细信息?

标签: identityserver4 openid-connect


【解决方案1】:

您正在使用由 OAuth 2.0 定义的 client credential grant

在您的代码中,RequestClientCredentialsTokenAsync 代表使用此授权类型的令牌请求。请注意,当您通过此授权获得访问令牌时,没有最终用户参与。它不是 OpenID Connect(没有经过身份验证的最终用户),仅适用于客户端凭据,只允许您拥有访问令牌。

现在,当您将此访问令牌用于 UserInfo request 时,身份服务器会检测到它没有任何相关的最终用户。因此它返回禁止响应,让您知道您没有访问端点的权限。

查看文档中的确切信息,

UserInfo 端点可用于检索有关用户的身份信息(请参阅spec)。

调用者需要发送代表用户的有效访问令牌

如果您需要用户信息,请使用授权码授予或范围值为 openid 的混合流来启用 OpenID Connect 请求。您可以阅读更多 here 以了解不同的授权类型。

【讨论】:

  • 我猜你是对的。但是我遇到的问题是我试图通过 Microsoft.Owin.Security 命名空间获取访问令牌,而 var accessToken = await HttpContext.GetTokenAsync("access_token") 需要 Microsoft.AspNetCore.Authentication 这在我的客户端应用程序。那么我该如何解决这个问题呢?也许我不能?
  • @arame3333 这是我不知道的。但我检查了你的其他问题。如果您担心注销,那么您可以简单地调用 IdentityServer 的结束会话端点。请阅读更多 - docs.identityserver.io/en/latest/topics/…
  • 无论如何,作为一个在许多平台上实现过客户端的人,我的建议是不要担心框架,因为大多数涉及的服务器调用都是易于理解的 HTTP 调用。唯一的承诺就是稍微阅读一下规范。
  • 我阅读了规范以获取原始代码。但是,我不明白访问令牌会彼此不同,但我想这一定是这种情况,因为授权做不同的事情。当我查看令牌端点的规范时,没有混合令牌的规范
  • @arame3333 是的,访问令牌将根据所使用的授权设置不同的范围(权限)。目前您正在使用客户端凭据授予,它不允许您访问任何用户信息(因为没有用户参与)。混合流是由 OpenID Connect (openid.net/specs/openid-connect-core-1_0.html#HybridFlowSteps) 定义的一种。您可能不需要深入了解该流程。而是使用许多客户端库支持的授权代码流。
猜你喜欢
  • 2019-12-11
  • 1970-01-01
  • 1970-01-01
  • 2021-03-27
  • 2020-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多