如何将声明包含到从授权端点检索的访问令牌中?

Posted

技术标签:

【中文标题】如何将声明包含到从授权端点检索的访问令牌中?【英文标题】:How do I include claims into the Access Token retrieved from the Authorize endpoint? 【发布时间】:2016-10-16 22:50:36 【问题描述】:

我想在通过 IdSrv 进行身份验证时检索到的访问令牌中包含默认声明。声明Ticket 应始终包含在访问令牌中,因此我创建了一个新的资源范围,并将Ticket 包含为ScopeClaim。但是,正如您从 IdSrv 日志中看到的那样,当 IdSrv 创建访问令牌并调用 GetProfileDataAsync 时,上下文中请求的声明列表为空,因此没有声明被添加到访问令牌中。

如何在默认情况下将此声明包含到访问令牌中?

我的印象是,请求 Resource 范围将允许范围内的声明作为访问令牌的一部分返回。

范围

        public static List<Scope> Get()
        
            return new List<Scope>
            
                StandardScopes.OpenId //standard scope when dealing with open id connect
                ,
                StandardScopes.OfflineAccess
                ,
                new Scope
                
                    Name = "App",
                    DisplayName = "App",
                    Type = ScopeType.Identity,
                    Claims = new List<ScopeClaim>
                    
                        new ScopeClaim
                        
                            AlwaysIncludeInIdToken = false,
                            Name = "App",
                            Description = "Role Information"
                        ,
//                        new ScopeClaim
//                        
//                            AlwaysIncludeInIdToken = false,
//                            Name = "Ticket",
//                            Description = "Login ticket"
//                        
                    ,
                    IncludeAllClaimsForUser = false
                ,
                new Scope
                
                    Name = "AppAccess",
                    DisplayName = "AppAccess",
                    Type = ScopeType.Resource,
                    Claims = new List<ScopeClaim>
                    
                        new ScopeClaim
                        
                            Name = "Ticket",
                            Description = "Login ticket",
                        
                    ,
                    IncludeAllClaimsForUser = true
                
            ;

索赔

    public static List<Client> Get()
    
        return new List<Client>
        
            new Client
            
                ClientName = "Hybrid Flow",
                ClientId = "apphybrid",
                Enabled = true,
                Flow = Flows.Hybrid,
                AllowAccessToAllScopes = true,
                IdentityTokenLifetime = 120,
                AccessTokenLifetime = 400,
                RequireConsent = false,
                ClientSecrets = new List<Secret>
                
                    new Secret("secret".Sha256())
                ,
                RedirectUris = new List<string>
                
                    "localhost/App/login/Login.mr"
                ,
                PostLogoutRedirectUris = new List<string>
                
                    "localhost/App/login/Login.mr"
                ,
                AllowedScopes = new List<string>
                
                    Constants.StandardScopes.OpenId,
                    Constants.StandardScopes.OfflineAccess,
                    "App",
                    "AppAccess"
                
            
        ;
    

IDSrv 配置

public override Task AuthenticateLocalAsync(LocalAuthenticationContext context)
        
            var securityServiceProxy = new SecurityServiceProxy(new ServiceHeadersParameters  UserHostAddress = Ctx.Request.Host.Value );

            var ticket = securityServiceProxy.UseServiceClient(serviceClient => serviceClient.AuthenticateUser(context.UserName, context.Password, Configuration.ProviderCode));

            if (!ticket.IsValid())
            
                context.AuthenticateResult = new AuthenticateResult("Invalid credentials");
                return Task.FromResult(0);
            

            var claims = new List<Claim> 
                new Claim(GlobalConstant.TicketClaim, ticket.Ticket.ToString())
            ;

            context.AuthenticateResult = new AuthenticateResult(
                ticket.UserObjId.ToString(),
                context.UserName,
                claims: claims,
                authenticationMethod: Constants.AuthenticationMethods.Password,
                identityProvider: Configuration.ProviderCode
                );

            return Task.FromResult(0);
        

        public override Task GetProfileDataAsync(ProfileDataRequestContext context)
        
            var applicationDto = GetApplicationDto(context);

            var claims = new List<Claim>
            
                new Claim(Constants.ClaimTypes.Subject, context.Subject.GetSubjectId()),
            ;

            Log.Debug("The requested claims...");
            if (context.RequestedClaimTypes == null)
            
                Log.Debug("Requested Claims is null");
            
            else
            
                foreach (var x in context.RequestedClaimTypes)
                
                    Log.Debug($"Claims x");
                

                if (context.RequestedClaimTypes.Contains(GlobalConstant.TicketClaim))
                    claims.Add(context.Subject.Claims.Where(x => x.Type.Equals(GlobalConstant.TicketClaim)).FirstOrDefault());

                if (context.RequestedClaimTypes.Contains(GlobalConstant.ApplicationClaim))
                    claims.Add(new Claim(GlobalConstant.ApplicationClaim, applicationDto.Jsonify()));
            

            // set the issued claims - these are the ones that were requested, if available
            context.IssuedClaims = claims;
            Log.Debug("Finished ProfileDataAsync");
            return Task.FromResult(0);
        

        private ApplicationDto GetApplicationDto(ProfileDataRequestContext context)
        
            var securityServiceProxy = new SecurityServiceProxy(new ServiceHeadersParameters  UserHostAddress = Ctx.Request.Host.Value );

            return securityServiceProxy.UseServiceClient(serviceClient => serviceClient.RetrieveAuthenticatedUser(GetUserTicketFromContext(context)));
        

        private static UserTicketDto GetUserTicketFromContext(ProfileDataRequestContext context)
        
            Log.Debug("The claims in the context...");
            foreach(var x in context.Subject.Claims)
            
                Log.Debug($"Cliams x.Type x.Value");
            

            var ticketString = context.Subject.Claims.Where(x => x.Type.Equals(GlobalConstant.TicketClaim)).FirstOrDefault()?.Value;
            var userIdString = context.Subject.GetSubjectId();

            Guid Ticket, UserId;

            if(Guid.TryParse(ticketString, out Ticket) && Guid.TryParse(userIdString, out UserId))
            
                return new UserTicketDto  Ticket = Ticket, UserObjId = UserId ;
            

            return new UserTicketDto();
        

网络配置

    JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();

    AntiForgeryConfig.UniqueClaimTypeIdentifier = IdentityModel.JwtClaimTypes.Name;

    app.Use(async (ctx, next) =>  await next(); );

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    
        AuthenticationType = "cookies"
    );

    app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    
        ClientId = OAuthConstant.Client,
        RedirectUri = "localhost/App/login/Login.mr",
        PostLogoutRedirectUri = "localhost/App/login/Login.mr",
        Authority = "localhost/Oauth2server/securetoken",
        SignInAsAuthenticationType = "Cookies",
        ResponseType = "token code id_token",
        Scope = "OpenId App offline_access AppAccess",
        ClientSecret = "secret",
        UseTokenLifetime = false,
        Notifications = new OpenIdConnectAuthenticationNotifications()
        
            AuthorizationCodeReceived = IdentityServerClient.HandleOther,
            SecurityTokenReceived = IdentityServerClient.HandleOther,
            MessageReceived = IdentityServerClient.HandleOther,
            AuthenticationFailed = IdentityServerClient.HandleOther,
            RedirectToIdentityProvider = IdentityServerClient.HandleRedirectToIdentityProvider,
            SecurityTokenValidated = IdentityServerClient.HandleSecurityTokenValidated
        
    );

日志

2016-06-15 12:04:51.943 -05:00 [Information] Login page submitted
2016-06-15 12:04:55.320 -05:00 [Information] Login credentials successfully validated by user service
2016-06-15 12:04:55.332 -05:00 [Information] Calling PostAuthenticateAsync on the user service
2016-06-15 12:04:55.338 -05:00 [Information] issuing primary signin cookie
2016-06-15 12:04:55.344 -05:00 [Information] redirecting to: http://localhost/OAuth2Server/securetoken/connect/authorize?client_id=apphybrid&redirect_uri=http:%2F%2Flocalhost%2Fapp%2FLogin%2FLogin.mr&response_mode=form_post&response_type=code id_token token&scope=App openid offline_access AppAccess&state=OpenIdConnect.AuthenticationProperties%3DebxFcJnjMiMq2m1gPqBsYlrBWdLct2kaJSYn-s0nxImnff-37i4t8Wa3wAJewJGFe9msgeeqJDKtR1gwwfA0e8Pdd6RNAi6YPo_CqT4l5zV8ifohYQVN9TrWfLXITXuKId9IW2cCeRQL6d8uWfkzSANqAGSbSGJYZ5pgOLULQresbAiJ7N77FgBmgrVtX4hDQuwGGL5vZFCb_C5tjl8_ezH12w8zQfifKuLwjaDmOSGYyo2AqpowQXXeSSSDgKBF&nonce=636016067018002117.MWY4MGVjOWItYTFjYS00MTVlLTg4MDYtMjYxYjkwMWEzNzU4ZWViNzEyNTQtMjE0Mi00MjYzLTk2ZjMtODdhYmIxYTM5Mjg5
2016-06-15 12:04:55.368 -05:00 [Debug] Incoming request: /securetoken/connect/authorize
2016-06-15 12:04:55.381 -05:00 [Information] Start authorize request
2016-06-15 12:04:55.381 -05:00 [Information] Start authorize request protocol validation
2016-06-15 12:04:55.381 -05:00 [Information] "Authorize request validation success"
 "
  \"ClientId\": \"apphybrid\",
  \"ClientName\": \"app Hybrid Flow\",
  \"RedirectUri\": \"http://localhost/app/Login/Login.mr\",
  \"AllowedRedirectUris\": [
    \"http://localhost/app/Login/Login.mr\"
  ],
  \"SubjectId\": \"783bf872-b864-4042-853d-04fbcb7a505a\",
  \"ResponseType\": \"code id_token token\",
  \"ResponseMode\": \"form_post\",
  \"Flow\": \"Hybrid\",
  \"RequestedScopes\": \"app openid offline_access appAccess\",
  \"State\": \"OpenIdConnect.AuthenticationProperties=ebxFcJnjMiMq2m1gPqBsYlrBWdLct2kaJSYn-s0nxImnff-37i4t8Wa3wAJewJGFe9msgeeqJDKtR1gwwfA0e8Pdd6RNAi6YPo_CqT4l5zV8ifohYQVN9TrWfLXITXuKId9IW2cCeRQL6d8uWfkzSANqAGSbSGJYZ5pgOLULQresbAiJ7N77FgBmgrVtX4hDQuwGGL5vZFCb_C5tjl8_ezH12w8zQfifKuLwjaDmOSGYyo2AqpowQXXeSSSDgKBF\",
  \"Nonce\": \"636016067018002117.MWY4MGVjOWItYTFjYS00MTVlLTg4MDYtMjYxYjkwMWEzNzU4ZWViNzEyNTQtMjE0Mi00MjYzLTk2ZjMtODdhYmIxYTM5Mjg5\",
  \"SessionId\": \"e79cd97a339b4513b45038e7755c1b88\",
  \"Raw\": 
    \"client_id\": \"apphybrid\",
    \"redirect_uri\": \"http://localhost/app/Login/Login.mr\",
    \"response_mode\": \"form_post\",
    \"response_type\": \"code id_token token\",
    \"scope\": \"app openid offline_access appAccess\",
    \"state\": \"OpenIdConnect.AuthenticationProperties=ebxFcJnjMiMq2m1gPqBsYlrBWdLct2kaJSYn-s0nxImnff-37i4t8Wa3wAJewJGFe9msgeeqJDKtR1gwwfA0e8Pdd6RNAi6YPo_CqT4l5zV8ifohYQVN9TrWfLXITXuKId9IW2cCeRQL6d8uWfkzSANqAGSbSGJYZ5pgOLULQresbAiJ7N77FgBmgrVtX4hDQuwGGL5vZFCb_C5tjl8_ezH12w8zQfifKuLwjaDmOSGYyo2AqpowQXXeSSSDgKBF\",
    \"nonce\": \"636016067018002117.MWY4MGVjOWItYTFjYS00MTVlLTg4MDYtMjYxYjkwMWEzNzU4ZWViNzEyNTQtMjE0Mi00MjYzLTk2ZjMtODdhYmIxYTM5Mjg5\"
  
"
2016-06-15 12:04:55.399 -05:00 [Information] Creating Hybrid Flow response.
2016-06-15 12:04:55.412 -05:00 [Information] Creating Implicit Flow response.
2016-06-15 12:04:55.416 -05:00 [Debug] Creating access token
2016-06-15 12:04:55.424 -05:00 [Debug] Getting ProfileDataAsync
2016-06-15 12:04:55.436 -05:00 [Debug] The claims in the context...
2016-06-15 12:04:55.437 -05:00 [Debug] Cliams sub 783bf872-b864-4042-853d-04fbcb7a505a
2016-06-15 12:04:55.437 -05:00 [Debug] Cliams name dev.guser
2016-06-15 12:04:55.437 -05:00 [Debug] Cliams amr password
2016-06-15 12:04:55.437 -05:00 [Debug] Cliams idp IDSRV
2016-06-15 12:04:55.437 -05:00 [Debug] Cliams auth_time 1466010295
2016-06-15 12:04:55.437 -05:00 [Debug] Cliams ticket fc05cd84-7756-4ec5-ac3c-53ac6d4d5e2a
2016-06-15 12:04:55.975 -05:00 [Debug] The requseted claims...
2016-06-15 12:04:55.976 -05:00 [Debug] Requested Claims Is Null
2016-06-15 12:04:55.976 -05:00 [Debug] Finished ProfileDataAsync
2016-06-15 12:04:55.982 -05:00 [Debug] Creating JWT access token
2016-06-15 12:04:56.049 -05:00 [Debug] Creating identity token
2016-06-15 12:04:56.054 -05:00 [Information] Getting claims for identity token for subject: 783bf872-b864-4042-853d-04fbcb7a505a
2016-06-15 12:04:56.054 -05:00 [Debug] Getting ProfileDataAsync
2016-06-15 12:04:56.066 -05:00 [Debug] The claims in the context...
2016-06-15 12:04:56.066 -05:00 [Debug] Cliams sub 783bf872-b864-4042-853d-04fbcb7a505a
2016-06-15 12:04:56.066 -05:00 [Debug] Cliams name dev.guser
2016-06-15 12:04:56.066 -05:00 [Debug] Cliams amr password
2016-06-15 12:04:56.066 -05:00 [Debug] Cliams idp IDSRV
2016-06-15 12:04:56.066 -05:00 [Debug] Cliams auth_time 1466010295
2016-06-15 12:04:56.066 -05:00 [Debug] Cliams ticket fc05cd84-7756-4ec5-ac3c-53ac6d4d5e2a
2016-06-15 12:04:56.338 -05:00 [Debug] The requseted claims...
2016-06-15 12:04:56.338 -05:00 [Debug] Cliams sub
2016-06-15 12:04:56.338 -05:00 [Debug] Finished ProfileDataAsync
2016-06-15 12:04:56.338 -05:00 [Debug] Creating JWT identity token
2016-06-15 12:04:56.344 -05:00 [Debug] Adding client "apphybrid" to client list cookie for subject "783bf872-b864-4042-853d-04fbcb7a505a"
2016-06-15 12:04:56.349 -05:00 [Information] End authorize request
2016-06-15 12:04:56.352 -05:00 [Information] Posting to http://localhost/app/Login/Login.mr
2016-06-15 12:04:56.352 -05:00 [Debug] Using AssetManager to render authorization response html
2016-06-15 12:04:56.388 -05:00 [Debug] Incoming request: /securetoken/assets/app.FormPostResponse.js

//Web 
2016-06-15 12:04:56,422 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Triggered MessageReceivedNotification`2 notification
2016-06-15 12:04:56,426 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Triggered SecurityTokenReceivedNotification`2 notification
2016-06-15 12:04:56,487 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Triggered SecurityTokenValidated notification
2016-06-15 12:04:56,487 [16] DEBUG app.Web.IdentityServer.IdentityServerClient The Claims in the identity
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: iss http://localhost/OAuth2Server/securetoken
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: aud apphybrid
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: exp 1466010416
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: nbf 1466010296
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: nonce 636016067018002117.MWY4MGVjOWItYTFjYS00MTVlLTg4MDYtMjYxYjkwMWEzNzU4ZWViNzEyNTQtMjE0Mi00MjYzLTk2ZjMtODdhYmIxYTM5Mjg5
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: iat 1466010296
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: at_hash 6pIu3P1cEeTQJMcK8Gcnhw
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: c_hash VsSw9HC0xyodlSkSCZefLw
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: sid e79cd97a339b4513b45038e7755c1b88
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: sub 783bf872-b864-4042-853d-04fbcb7a505a
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: auth_time 1466010295
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: idp IDSRV
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Claims: amr password
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient The access token: [Omitted]
2016-06-15 12:04:56,488 [16] DEBUG app.Web.IdentityServer.IdentityServerClient The id token: [Omitted]
2016-06-15 12:04:56,491 [16] DEBUG app.Web.IdentityServer.IdentityServerClient Triggered AuthenticationFailedNotification`2 notification
//Web

//Logs

有问题的日志输出...

2016-06-15 12:04:55.412 -05:00 [Information] Creating Implicit Flow response.
2016-06-15 12:04:55.416 -05:00 [Debug] Creating access token
2016-06-15 12:04:55.424 -05:00 [Debug] Getting ProfileDataAsync
2016-06-15 12:04:55.436 -05:00 [Debug] The claims in the context...
2016-06-15 12:04:55.437 -05:00 [Debug] Cliams sub 783bf872-b864-4042-853d-04fbcb7a505a
2016-06-15 12:04:55.437 -05:00 [Debug] Cliams name dev.guser
2016-06-15 12:04:55.437 -05:00 [Debug] Cliams amr password
2016-06-15 12:04:55.437 -05:00 [Debug] Cliams idp IDSRV
2016-06-15 12:04:55.437 -05:00 [Debug] Cliams auth_time 1466010295
2016-06-15 12:04:55.437 -05:00 [Debug] Cliams ticket fc05cd84-7756-4ec5-ac3c-53ac6d4d5e2a    <- Has the claim I want
2016-06-15 12:04:55.975 -05:00 [Debug] The requseted claims...
2016-06-15 12:04:55.976 -05:00 [Debug] Requested Claims Is Null    <- but this needs to indicate that we want that claim...
2016-06-15 12:04:55.976 -05:00 [Debug] Finished ProfileDataAsync

【问题讨论】:

【参考方案1】:

更新 2:好的,现在我仔细观察,你有 IncludeAllClaimsForUser = true。在 GetProfileData 上下文中有一个类似的标志。我怀疑这就是为什么您在集合中没有声明。

更新 1:您想要进入令牌的声明应添加到 GetProfile 而不是 AuthentivcateLocal 方法的 AuthenticateResult 中。

原文:将作用域的类型从Identity 更改为Resource。这会影响声明进入哪些令牌。

【讨论】:

作用域类型是Resource,对于AppAccess作用域 啊,我在看 App 范围。 已更新以回答问题。 感谢您的更新,我知道这些声明应该添加到 GetProfile() 但我想尊重 RequestedClaims 框架,而不是一直包含声明。问题是当我从 IdSrv 内部点击 UserInfo 端点时(当它开始构建令牌时)RequestedClaims 列表为空。我可以一直添加Ticket 声明,但这违背了拥有此端点的目的...... 我编辑了问题以使问题中的日志更加可见

以上是关于如何将声明包含到从授权端点检索的访问令牌中?的主要内容,如果未能解决你的问题,请参考以下文章

如何让我的 Auth0 权限进入 AWS HTTP API Jwt 授权方的访问令牌的范围声明?

如何使用代码授权流在 Spring 应用程序中提取 Oauth2 访问令牌?

如何将身份验证令牌传递给端点?

我无法从 Spotify 为我的应用检索访问令牌

从 URL Azure AD 捕获访问令牌以用于令牌

更新长寿命访问令牌服务器端