gpt4 book ai didi

c# - 身份服务器 : Add claims to access token in hybrid flow in MVC client

转载 作者:太空狗 更新时间:2023-10-29 22:01:58 25 4
gpt4 key购买 nike

我已阅读文档并按照示例进行操作,但我无法将用户声明获取到访问 token 中。我的客户端不是 ASP.NET Core,因此 MVC 客户端的配置与 v4 示例不同。

除非我误解了文档,否则 ApiResources 用于在创建访问 token 时填充配置文件服务中的 RequestedClaimTypes。客户端应该将 api 资源添加到它的范围列表中以包含关联的用户声明。在我的例子中,它们没有连接。

当使用“ClaimsProviderAccessToken”的调用方调用 ProfileService.GetProfileDataAsync 时,请求的声明类型为空。即使我在此处设置了 context.IssuedClaims,当再次调用“AccessTokenValidation”时,也不会设置对上下文的声明。

在 MVC 应用中:

    app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
UseTokenLifetime = false,
ClientId = "portal",
ClientSecret = "secret",
Authority = authority,
RequireHttpsMetadata = false,
RedirectUri = redirectUri,
PostLogoutRedirectUri = postLogoutRedirectUri,
ResponseType = "code id_token",
Scope = "openid offline_access portal",
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
await AssembleUserClaims(n);
},
RedirectToIdentityProvider = n =>
{
// if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectRequestType.Logout)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");

if (idTokenHint != null)
{
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}

}

return Task.FromResult(0);
}
}
});

private static async Task AssembleUserClaims(AuthorizationCodeReceivedNotification notification)
{

string authCode = notification.ProtocolMessage.Code;

string redirectUri = "https://myuri.com";

var tokenClient = new TokenClient(tokenendpoint, "portal", "secret");

var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(authCode, redirectUri);

if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}

// use the access token to retrieve claims from userinfo
var userInfoClient = new UserInfoClient(new Uri(userinfoendpoint), tokenResponse.AccessToken);

var userInfoResponse = await userInfoClient.GetAsync();

// create new identity
var id = new ClaimsIdentity(notification.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(userInfoResponse.GetClaimsIdentity().Claims);
id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
id.AddClaim(new Claim("id_token", notification.ProtocolMessage.IdToken));
id.AddClaim(new Claim("sid", notification.AuthenticationTicket.Identity.FindFirst("sid").Value));
notification.AuthenticationTicket = new AuthenticationTicket(id, notification.AuthenticationTicket.Properties);
}

身份服务器客户端:

    private Client CreatePortalClient(Guid tenantId)
{
Client portal = new Client();
portal.ClientName = "Portal MVC";
portal.ClientId = "portal";
portal.ClientSecrets = new List<Secret> { new Secret("secret".Sha256()) };
portal.AllowedGrantTypes = GrantTypes.HybridAndClientCredentials;
portal.RequireConsent = false;
portal.RedirectUris = new List<string> {
"https://myuri.com",
};
portal.AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"portal"
};
portal.Enabled = true;
portal.AllowOfflineAccess = true;
portal.AlwaysSendClientClaims = true;
portal.AllowAccessTokensViaBrowser = true;

return portal;
}

API 资源:

public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource
{
Name= "portalresource",
UserClaims = { "tenantId","userId","user" },
Scopes =
{
new Scope()
{
Name = "portalscope",
UserClaims = { "tenantId","userId","user",ClaimTypes.Role, ClaimTypes.Name),

},

}
},

};
}

身份资源:

    public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new IdentityResource[]
{
// some standard scopes from the OIDC spec
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
new IdentityResource("portal", new List<string>{ "tenantId", "userId", "user", "role", "name"})
};
}

更新:

这是 MVC 应用程序和身份服务器 (IS) 之间的交互:

MVC: 
Owin Authentication Challenge
IS:
AccountController.LoginAsync - assemble user claims and call HttpContext.SignInAsync with username and claims)
ProfileService.IsActiveAsync - Context = "AuthorizeEndpoint", context.Subject.Claims = all userclaims
ClaimsService.GetIdentityTokenClaimsAsync - Subject.Claims (all userclaims), resources = 1 IdentityResource (OpenId), GrantType = Hybrid
MVC:
SecurityTokenValidated (Notification Callback)
AuthorizationCodeReceived - Protocol.Message has Code and IdToken call to TokenClient.RequestAuthorizationCodeAsync()
IS:
ProfileService.IsActiveAsync - Context = "AuthorizationCodeValidation", context.Subject.Claims = all userclaims
ClaimsService.GetAccessTokenClaimsAsync - Subject.Claims (all userclaims), resources = 2 IdentityResource (openId,profile), GrantType = Hybrid
ProfileService.GetProfileDataAsync - Context = "ClaimsProviderAccessToken", context.Subject.Claims = all userclaims, context.RequestedClaimTypes = empty, context.IssuedClaims = name,role,user,userid,tenantid
ClaimsService.GetIdentityTokenClaimsAsync - Subject.Claims (all userclaims), resources = 2 IdentityResource (openId,profile), GrantType = authorization_code

MVC:
call to UserInfoClient with tokenResponse.AccessToken
IS:
ProfileService.IsActiveAsync - Context = "AccessTokenValidation", context.Subject.Claims = sub,client_id,aud,scope etc (expecting user and tenantId here)
ProfileService.IsActiveAsync - Context = "UserInfoRequestValidation", context.Subject.Claims = sub,auth_time,idp, amr
ProfileService.GetProfileDataAsync - Context = "UserInfoEndpoint", context.Subject.Claims = sub,auth_time,idp,amp, context.RequestedClaimTypes = sub

最佳答案

因为我没有看到您的 await AssembleUserClaims(context); 中发生了什么我建议检查它是否在执行以下操作:

基于您从 context.ProtoclMessage.AccessToken 获得的访问 token 或从调用 TokenEndpoint你应该创建一个新的 ClaimsIdentity .你这样做是因为你没有提到它吗?

像这样:

var tokenClient = new TokenClient(
IdentityServerTokenEndpoint,
"clientId",
"clientSecret");


var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(
n.Code, n.RedirectUri);

if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}

// create new identity
var id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);

id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
id.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
id.AddClaims(n.AuthenticationTicket.Identity.Claims);

// get user info claims and add them to the identity
var userInfoClient = new UserInfoClient(IdentityServerUserInfoEndpoint);
var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);
var userInfoEndpointClaims = userInfoResponse.Claims;

// this line prevents claims duplication and also depends on the IdentityModel library version. It is a bit different for >v2.0
id.AddClaims(userInfoEndpointClaims.Where(c => id.Claims.Any(idc => idc.Type == c.Type && idc.Value == c.Value) == false));

// create the authentication ticket
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
n.AuthenticationTicket.Properties);

还有一件事 - 阅读 this关于资源。在您的特定情况下,您关心 IdentityResources(但我看到您也有它)。

所以 - 当调用 UserInfoEndpoint 时您看到响应中的声明了吗?如果没有 - 那么问题是它们没有发行。

检查这些,我们可以深入挖掘。

祝你好运

编辑

我有一个您可能喜欢或不喜欢的解决方案,但我会推荐它。

在 IdentityServer 项目中,在 AccountController.cs有一种方法public async Task<IActionResult> Login(LoginInputModel model, string button) .

这是用户单击登录页面(或您在那里的任何自定义页面)上的登录按钮后的方法。

在这个方法中有一个调用await HttpContext.SignInAsync .此调用接受用户主题、用户名、身份验证属性和声明列表 参数。在这里您可以添加您的自定义声明,然后当您在 AuthorizationCodeReceived 中调用 userinfo 端点时它将出现.我刚刚测试了这个并且它有效。

其实我发现这是添加自定义声明的方法。否则 - IdentityServer 不知道您的自定义声明,并且无法用值填充它们。尝试一下,看看它是否适合您。

关于c# - 身份服务器 : Add claims to access token in hybrid flow in MVC client,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49274473/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com