【问题标题】:Refresh token with OpenIddict returns always principal to null使用 OpenIddict 刷新令牌始终返回主体为 null
【发布时间】:2021-08-08 02:11:38
【问题描述】:

自从从 OpenIdDict 2.x 迁移到 3.X 后,我们无法再使用 refreshtoken。 我们的代码基于dotnet core 3.1

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

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

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

错误信息

System.ArgumentNullException:值不能为空。 (范围 '校长')

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

startup.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

[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 的示例。

我真的不明白怎么了

【问题讨论】:

  • 你好@james.lee,我也有同样的问题。你找到解决办法了吗?
  • @Gringo 这是我的问题,没有得到答案。凯文(创作者)告诉我他会回答,但从那以后就没有消息了。

标签: c# asp.net asp.net-identity openiddict


【解决方案1】:

尝试将 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);
        }

【讨论】:

  • 它不起作用,因为 info.Principal 为空(这是我的代码引发异常的地方)
猜你喜欢
  • 2017-05-09
  • 2013-05-10
  • 2018-03-22
  • 2018-03-16
  • 2016-01-05
  • 2021-06-02
  • 2017-07-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多