繁体   English   中英

使用 OpenIddict 刷新令牌始终返回 null

[英]Refresh token with OpenIddict returns always principal to null

由于从 OpenIdDict 2.x 迁移到 3.X,我们不能再使用 refreshtoken。 我们的代码基于 dotnet core 3.1

用户/密码的处理工作正常,用户收到他的令牌(访问、ID 和刷新)

但是只要我们想发送一个刷新令牌,从用户那里获取主体的过程就会返回一个 null。

var info = await HttpContext.AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); var user = await _userManager.GetUserAsync(info.Principal);

错误信息

System.ArgumentNullException:值不能为 null。 (参数“主要”)

这是我们用来引导 Openiddict 的代码

启动.cs

services.AddOpenIddict()
    .AddCore(options =>
    {
        options.UseEntityFrameworkCore()
            .UseDbContext<DataContext>();
    })
    .AddServer(options =>
    {
        options.SetTokenEndpointUris("/connect/token")
            .AllowPasswordFlow()
            .AllowRefreshTokenFlow();

        options.SetAccessTokenLifetime(TimeSpan.FromDays(1));
        options.SetRefreshTokenLifetime(TimeSpan.FromDays(1));
        options.AcceptAnonymousClients();
        options.AddDevelopmentEncryptionCertificate();
        options.AddDevelopmentSigningCertificate();
        options.RequireProofKeyForCodeExchange();

        options.UseAspNetCore()
            .EnableTokenEndpointPassthrough();
    })
    .AddValidation(options =>
    {
        options.UseLocalServer();
        options.UseAspNetCore();
    });

授权控制器.cs

[HttpPost("~/connect/token")]
public async Task<IActionResult> Login()
{
    var request = HttpContext.GetOpenIddictServerRequest() ??
        throw new InvalidOperationException(
            "The OpenID Connect request cannot be retrieved.");

    if (request.IsPasswordGrantType())
    {
        return await CheckPasswordGrantType(request);
    }

    if (request.IsRefreshTokenGrantType())
    {
        return await CheckRefreshTokenGrantType();
    }

    throw new NotImplementedException("The specified grant type is not implemented.");
}

private async Task<IActionResult> CheckRefreshTokenGrantType()
{
    var info = await HttpContext.AuthenticateAsync(
        OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);

    var user = await _userManager.GetUserAsync(info.Principal);
    if (user == null)
    {
        var properties = new AuthenticationProperties(new Dictionary<string, string>
        {
            [OpenIddictServerAspNetCoreConstants.Properties.Error] =
                OpenIddictConstants.Errors.InvalidGrant,
            [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                "The refresh token is no longer valid."
        });

        return Forbid(properties, 
            OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
    }

    if (user.EmailConfirmed == false)
    {
        var properties = new AuthenticationProperties(new Dictionary<string, string>
        {
            [OpenIddictServerAspNetCoreConstants.Properties.Error] =
                OpenIddictConstants.Errors.InvalidGrant,
            [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                "Email address not confirmed."
        });

        return Forbid(properties, 
            OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
    }

    if (!await _signInManager.CanSignInAsync(user))
    {
        var properties = new AuthenticationProperties(new Dictionary<string, string>
        {
            [OpenIddictServerAspNetCoreConstants.Properties.Error] =
                OpenIddictConstants.Errors.InvalidGrant,
            [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                "The user is no longer allowed to sign in."
        });

        return Forbid(properties, 
            OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
    }

    foreach (var claim in info.Principal.Claims)
    {
        claim.SetDestinations(GetDestinations(claim, info.Principal));
    }

    return SignIn(info.Principal, 
        OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}

private async Task<IActionResult> CheckPasswordGrantType(OpenIddictRequest request)
{
    var user = await _userManager.FindByNameAsync(request.Username);
    if (user == null)
    {
        var properties = new AuthenticationProperties(new Dictionary<string, string>
        {
            [OpenIddictServerAspNetCoreConstants.Properties.Error] = 
            OpenIddictConstants.Errors.InvalidGrant,
            [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                "The username/password couple is invalid."
        });

        return Forbid(properties, 
            OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
    }

    if (user.EmailConfirmed == false)
    {
        var properties = new AuthenticationProperties(new Dictionary<string, string>
        {
            [OpenIddictServerAspNetCoreConstants.Properties.Error] = 
            OpenIddictConstants.Errors.AccessDenied,
            [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = 
            "Email address not confirmed."
        });

        return Forbid(properties, 
            OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
    }

    var result = 
        await _signInManager.CheckPasswordSignInAsync(user, request.Password, true);
    if (!result.Succeeded)
    {
        if (result.IsLockedOut)
        {
            var properties =
                new AuthenticationProperties(new Dictionary<string, string>
            {
                [OpenIddictServerAspNetCoreConstants.Properties.Error] =
                    OpenIddictConstants.Errors.InvalidRequest,
                [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                    "Account locked : too many attempts."
            });

            return Forbid(properties, 
                OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
        }
        else
        {
            var properties = 
                new AuthenticationProperties(new Dictionary<string, string>
            {
                [OpenIddictServerAspNetCoreConstants.Properties.Error] =
                    OpenIddictConstants.Errors.InvalidGrant,
                [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                    "The username/password couple is invalid."
            });
            return Forbid(properties, 
                OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
        }
    }

    var principal = await _signInManager.CreateUserPrincipalAsync(user);
    principal.SetScopes(new[]
    {
                OpenIddictConstants.Scopes.OpenId,
                OpenIddictConstants.Scopes.Email,
                OpenIddictConstants.Scopes.Profile,
                OpenIddictConstants.Scopes.Roles,
                OpenIddictConstants.Scopes.OfflineAccess
            }.Intersect(request.GetScopes()));

    foreach (var claim in principal.Claims)
    {
        claim.SetDestinations(GetDestinations(claim, principal));
    }

    return SignIn(principal, 
    OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}

private IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal)
{
    switch (claim.Type)
    {
        case OpenIddictConstants.Claims.Name:
            yield return OpenIddictConstants.Destinations.AccessToken;

            if (principal.HasScope(OpenIddictConstants.Scopes.Profile))
                yield return OpenIddictConstants.Destinations.IdentityToken;

            yield break;

        case OpenIddictConstants.Claims.Email:
            yield return OpenIddictConstants.Destinations.AccessToken;

            if (principal.HasScope(OpenIddictConstants.Scopes.Email))
                yield return OpenIddictConstants.Destinations.IdentityToken;

            yield break;

        case OpenIddictConstants.Claims.Role:
            yield return OpenIddictConstants.Destinations.AccessToken;

            if (principal.HasScope(OpenIddictConstants.Scopes.Roles))
                yield return OpenIddictConstants.Destinations.IdentityToken;

            yield break;

        case "AspNet.Identity.SecurityStamp": yield break;

        default:
            yield return OpenIddictConstants.Destinations.AccessToken;
            yield break;
    }
}

大部分代码来自 OpenIddict 的示例。

我真的不明白怎么了

尝试将 setCopes 也添加到 CheckRefreshTokenGrantType,我使用它,它对我有用:

if (request.IsRefreshTokenGrantType())
        {
            // Retrieve the claims principal stored in the refresh token.
            var info = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

            // Retrieve the user profile corresponding to the refresh token.
            // Note: if you want to automatically invalidate the refresh token
            // when the user password/roles change, use the following line instead:
            // var user = _signInManager.ValidateSecurityStampAsync(info.Principal);
            var user = await _userManager.GetUserAsync(info.Principal);
            if (user == null)
            {
                var properties = new AuthenticationProperties(new Dictionary<string, string>
                {
                    [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
                    [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The refresh token is no longer valid."
                });

                return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
            }

            // Ensure the user is still allowed to sign in.
            if (!await _signInManager.CanSignInAsync(user))
            {
                var properties = new AuthenticationProperties(new Dictionary<string, string>
                {
                    [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
                    [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in."
                });

                return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
            }

            // Create a new ClaimsPrincipal containing the claims that
            // will be used to create an id_token, a token or a code.
            var principal = await _signInManager.CreateUserPrincipalAsync(user);

            //Add claims to the LocalTokenValidationApi 
            principal.SetResources("LocalTokenValidationApi");

            // Set the list of scopes granted to the client application.
            principal.SetScopes(new[]
            {
                Scopes.OpenId,
                Scopes.Email,
                Scopes.Profile,
                Scopes.Roles,
                Scopes.OfflineAccess
            }.Intersect(request.GetScopes()));

            foreach (var claim in principal.Claims)
            {
                claim.SetDestinations(GetDestinations(claim, principal));
            }

            return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
        }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM