简体   繁体   English

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

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

Since the migration from OpenIdDict 2.x to 3.X, we are not able to use refreshtoken anymore.由于从 OpenIdDict 2.x 迁移到 3.X,我们不能再使用 refreshtoken。 Our code is based on dotnet core 3.1我们的代码基于 dotnet core 3.1

The processing of user/password works fine and the user receives his tokens (access, id and refresh)用户/密码的处理工作正常,用户收到他的令牌(访问、ID 和刷新)

But as soon as we want to send a refresh token, the process of getting the principal from the user returns a null.但是只要我们想发送一个刷新令牌,从用户那里获取主体的过程就会返回一个 null。

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

The error message错误信息

System.ArgumentNullException: Value cannot be null. System.ArgumentNullException:值不能为 null。 (Parameter 'principal') (参数“主要”)

Here is the code we use to bootstrap Openiddict这是我们用来引导 Openiddict 的代码

startup.cs启动.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();
    });

AuthorizationController.cs授权控制器.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;
    }
}

Most of the code is coming from the samples from OpenIddict.大部分代码来自 OpenIddict 的示例。

I really don't understand what's wrong我真的不明白怎么了

Try to add setCopes also to CheckRefreshTokenGrantType, I use it and it works for me:尝试将 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