我试图为我的aspnet核心网络应用生成访问令牌。我创建了以下提供程序:
public class CustomOpenIdConnectServerProvider : OpenIdConnectServerProvider
{
public override Task ValidateTokenRequest(ValidateTokenRequestContext context)
{
// Reject the token requests that don't use grant_type=password or grant_type=refresh_token.
if (!context.Request.IsPasswordGrantType() && !context.Request.IsRefreshTokenGrantType())
{
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
description: "Only the resource owner password credentials and refresh token " +
"grants are accepted by this authorization server");
return Task.FromResult(0);
}
// Since there's only one application and since it's a public client
// (i.e a client that cannot keep its credentials private), call Skip()
// to inform the server the request should be accepted without
// enforcing client authentication.
context.Skip();
return Task.FromResult(0);
}
public override async Task HandleTokenRequest(HandleTokenRequestContext context)
{
// Resolve ASP.NET Core Identity's user manager from the DI container.
var manager = context.HttpContext.RequestServices.GetRequiredService<UserManager<User>>();
// Only handle grant_type=password requests and let ASOS
// process grant_type=refresh_token requests automatically.
if (context.Request.IsPasswordGrantType())
{
var user = await manager.FindByNameAsync(context.Request.Username);
if (user == null)
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "Invalid credentials.");
return;
}
// Ensure the password is valid.
if (!await manager.CheckPasswordAsync(user, context.Request.Password))
{
if (manager.SupportsUserLockout)
{
await manager.AccessFailedAsync(user);
}
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "Invalid credentials.");
return;
}
if (manager.SupportsUserLockout)
{
await manager.ResetAccessFailedCountAsync(user);
}
var identity = new ClaimsIdentity(context.Options.AuthenticationScheme);
// Note: the name identifier is always included in both identity and
// access tokens, even if an explicit destination is not specified.
identity.AddClaim(ClaimTypes.NameIdentifier, await manager.GetUserIdAsync(user));
identity.AddClaim(OpenIdConnectConstants.Claims.Subject, await manager.GetUserIdAsync(user));
// When adding custom claims, you MUST specify one or more destinations.
// Read "part 7" for more information about custom claims and scopes.
identity.AddClaim("username", await manager.GetUserNameAsync(user),
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
var claims = await manager.GetClaimsAsync(user);
foreach (var claim in claims)
{
identity.AddClaim(claim.Type, claim.Value, OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Create a new authentication ticket holding the user identity.
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
context.Options.AuthenticationScheme);
// Set the list of scopes granted to the client application.
ticket.SetScopes(
/* openid: */ OpenIdConnectConstants.Scopes.OpenId,
OpenIdConnectConstants.Scopes.OfflineAccess,
/* email: */ OpenIdConnectConstants.Scopes.Email,
/* profile: */ OpenIdConnectConstants.Scopes.Profile);
// Set the resource servers the access token should be issued for.
ticket.SetResources("resource_server");
context.Validate(ticket);
}
}
这很好用,我可以获得访问令牌,并且用户已成功通过身份验证。我在这里遇到的问题是,当我执行此操作时,在任何授权的操作方法中:var user = await _userManager.GetUserAsync(User);
user
的值始终为null!当然,我使用有效的访问令牌传递Authorization标头,并且请求进入使用Authorize
注释的操作,没有任何问题。它只是user
的值为null。谁能告诉我我的代码有什么问题?
答案 0 :(得分:2)
默认情况下,UserManager.GetUserAsync(User)
使用ClaimTypes.NameIdentifier
声明作为用户标识符。
在您的情况下,ClaimTypes.NameIdentifier
- OpenID Connect服务器中间件不再将其视为1.0中的特殊声明 - 未添加到访问令牌中,因为它没有适当的目标。因此,Identity无法从访问令牌中提取用户标识符。
您有3个选项可以解决这个问题:
通过services.Configure<IdentityOptions>(options => options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject);
方法调用Startup.ConfigureServices()
,替换身份使用的默认用户标识符声明。
继续使用ClaimTypes.NameIdentifier
声明,但请将其设为正确的目的地(OpenIdConnectConstants.Destinations.AccessToken
)。
使用UserManager.FindByIdAsync(User.FindFirstValue(OpenIdConnectConstants.Claims.Subject))
代替UserManager.GetUserAsync(User)
。