简体   繁体   English

在 ASP.Net Core Identity 中刷新用户 cookie 票证

[英]Refresh user cookie ticket in ASP.Net Core Identity

In a controller in an ASP.NET Core web application I want to refresh the user and claims in the cookie ticket stored on the client.在 ASP.NET Core Web 应用程序的控制器中,我想刷新存储在客户端上的 cookie 票证中的用户和声明。

The client is authenticated and authorized, ASP.NET Core Identity stores this Information in the cookie ticket - now in some Controller actions I want to refresh the data in the cookie.客户端经过身份验证和授权,ASP.NET Core Identity 将此信息存储在 cookie 票证中 - 现在在某些控制器操作中,我想刷新 cookie 中的数据。

The SignInManager has a function to refresh RefreshSignInAsync , but it does not accept HttpContext.User as parameter. SignInManager具有刷新RefreshSignInAsync的功能,但它不接受HttpContext.User作为参数。

[HttpPost("[action]")]
[Authorize]
public async Task<IActionResult> Validate()
{
  // todo: update the Client Cookie
  await _signInManager.RefreshSignInAsync(User); // wrong type
}

How do I refresh the cookie?如何刷新cookie?

public static class HttpContextExtensions
{
    public static async Task RefreshLoginAsync(this HttpContext context)
    {
        if (context.User == null)
            return;

        // The example uses base class, IdentityUser, yours may be called 
        // ApplicationUser if you have added any extra fields to the model
        var userManager = context.RequestServices
            .GetRequiredService<UserManager<IdentityUser>>();
        var signInManager = context.RequestServices
            .GetRequiredService<SignInManager<IdentityUser>>();

        IdentityUser user = await userManager.GetUserAsync(context.User);

        if(signInManager.IsSignedIn(context.User))
        {
            await signInManager.RefreshSignInAsync(user);
        }
    }
}

Then use it in your controller然后在您的控制器中使用它

[HttpPost("[action]")]
[Authorize]
public async Task<IActionResult> Validate()
{
    await HttpContext.RefreshLoginAsync();
}

Or abstract it in an action filter或者在动作过滤器中抽象它

public class RefreshLoginAttribute : ActionFilterAttribute
{
    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        await context.HttpContext.RefreshLoginAsync();

        await next();
    }
}

Then use it like this in your controller然后在您的控制器中像这样使用它

[HttpPost("[action]")]
[Authorize]
[RefreshLogin] // or simpler [Authorize, RefreshLogin]
public async Task<IActionResult> Validate()
{
    // your normal controller code
}

This is also possible if the user is already logged out (their access token has expired but their refresh token is still valid).如果用户已经注销(他们的访问令牌已过期但他们的刷新令牌仍然有效),这也是可能的。

Important note: the following only works if you have "Do you want to remember your user's devices?"重要提示:仅当您有“您想记住用户的设备吗?”时,以下内容才有效。 set to "No" in the cognito config.在 cognito 配置中设置为“否”。 If anyone knows how to get it to work with it on, please let me know.如果有人知道如何使用它,请告诉我。

We use the following flow (js client app connecting to .NET Core API):我们使用以下流程(连接到 .NET Core API 的 js 客户端应用程序):

  1. User signs in using username/password ( CognitoSignInManager<CognitoUser>.PasswordSignInAsync )用户使用用户名/密码登录 ( CognitoSignInManager<CognitoUser>.PasswordSignInAsync )
  2. The client receives the token , userID , and refreshToken and stores them in localStorage.客户端接收tokenuserIDrefreshToken并将它们存储在 localStorage 中。
  3. When the original token expires (1 hour), the client gets a 401 error from the API.当原始令牌过期(1 小时)时,客户端从 API 收到 401 错误。
  4. The client calls another API endpoint with the userID and refreshToken which then in turn calls the code below on our user service.客户端使用userIDrefreshToken调用另一个 API 端点,然后它又在我们的用户服务上调用下面的代码。
  5. If the refresh result is successful, we return the new token ( AuthenticationResult.IdToken ).如果刷新结果成功,我们将返回新令牌( AuthenticationResult.IdToken )。
  6. The client the repeats the call that originally errored in a 401 with the new token.客户端使用新令牌重复最初在 401 中出错的调用。

Here is the code we added to the User Service:这是我们添加到用户服务中的代码:

public async Task<UserLoginResult> SignInRefreshAsync(string uid, string refreshToken)
{
    try
    {
        var result = await _cognitoIdentityProvider.InitiateAuthAsync(
            new InitiateAuthRequest
            {
                AuthFlow = AuthFlowType.REFRESH_TOKEN_AUTH,
                ClientId = _pool.ClientID,
                AuthParameters = new Dictionary<string, string>
                {
                    { "REFRESH_TOKEN", refreshToken },
                    { "SECRET_HASH", HmacSHA256(uid + _pool.ClientID, _options.UserPoolClientSecret) }
                }
            });

        if (!result.HttpStatusCode.Successful() || string.IsNullOrEmpty(result.AuthenticationResult?.IdToken))
            return new UserLoginResult(UserLoginStatus.Failed);

        return new UserLoginResult(UserLoginStatus.Success, uid, null, null, result.AuthenticationResult.IdToken, null);
    }
    catch
    {
        return new UserLoginResult(UserLoginStatus.Failed);
    }
}

private static string HmacSHA256(string data, string key)
{
    using (var sha = new System.Security.Cryptography.HMACSHA256(System.Text.Encoding.UTF8.GetBytes(key)))
    {
        var result = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(data));
        return Convert.ToBase64String(result);
    }
}

IAmazonCognitoIdentityProvider _cognitoIdentityProvider is resolved from DI. IAmazonCognitoIdentityProvider _cognitoIdentityProvider从 DI 解析。

AWSCognitoClientOptions _options = configuration.GetAWSCognitoClientOptions(); and IConfiguration configuration is also resolved from DI.并且IConfiguration configuration也是从 DI 解析的。

UserLoginResult is our class to hold the token and refresh token. UserLoginResult是我们用来保存令牌和刷新令牌的类。 Obviously, adjust accordingly.显然,相应地进行调整。

Please note that setting SECRET_HASH may not be required based on your config is Cognito.请注意,根据您的配置是 Cognito,可能不需要设置SECRET_HASH

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

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