[英]OpenIddict Asp.net Core 5.0 How to Get Principal Associated in Refresh Token Flow
[英]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.