使用AspNet.Security.OpenIdConnect.Server时,用户始终为null

时间:2017-06-18 01:48:03

标签: asp.net-mvc asp.net-core openid openid-connect openid-provider

我试图为我的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。谁能告诉我我的代码有什么问题?

1 个答案:

答案 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)