4

Token Refresh with Asp.NET Identity

 2 years ago
source link: https://dev.to/pellerex/token-refresh-with-asp-net-identity-5c5c
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Token Refresh with Asp.NET Identity

Token Refresh with Asp.NET Identity

Step by step guide on how refresh Asp.NET API Json Web Tokens (JWT)

The Need

Every properly issued JWT (Json Web Token) has an expiry date, generally within minutes of issuance, and once the expiry date is passed, the token becomes invalid and needs to be re-issued. If the token couldn’t be renewed, the user needs to be redirected to the Login page, to acquire their credentials again. In this post, I will cover how to re-issue a new token based on an expired one.

By the way, this topic belongs to the series to set up Authentication and Authorisation for Asp.NET and React ecosystems.

Asp.NET Identity comes with Token Validation Services. When I generate every pair of AccessToken and Refresh token at Login time, I also save them into the database using SetAuthenticationTokenAsync method of Asp.NET Identity UserManager, like the code below:

private readonly ILogger<AccountServices> logger; private readonly UserManager<ApplicationUser> userManager; private readonly SignInManager<ApplicationUser> signInManager;

public async Task<LoginResultViewModel> Login(LoginViewModel model) { var result = await signInManager.PasswordSignInAsync(model.Email, model.Password, false, false);

if (!result.Succeeded) { logger.LogError($"result of PasswordSignInAsync was failure"); return null; }

var user = await userManager.FindByEmailAsync(model.Email);

if (user.IsEmpty()) { logger.LogError($"user with email {model.Email} was null"); return null; }

var userClaims = await GetUserClaims(user);

var jwtResult = jwtAuthManager.GenerateTokens(user, userClaims, DateTime.Now); //save in db await userManager.SetAuthenticationTokenAsync( user, appSettings.AppName, appSettings.RefreshTokenName, jwtResult.RefreshToken.TokenString);

return new LoginResultViewModel() { User = new UserViewModel() { Email = model.Email, AccessToken = jwtResult.AccessToken, RefreshToken = jwtResult.RefreshToken.TokenString, FirstName = user.FirstName, LastName = user.LastName, Phone = user.PhoneNumber, UserId = user.Id } }; }

public async Task<IEnumerable<Claim>> GetUserClaims(ApplicationUser user) { var claims = new List<Claim> { new Claim(ClaimTypes.Name, $"{user.FirstName ?? string.Empty} {user.LastName ?? string.Empty}".Trim()), new Claim(ClaimTypes.GivenName, (user.FirstName ?? string.Empty).Trim()), new Claim(ClaimTypes.Surname, (user.LastName ?? string.Empty).Trim()), new Claim(ClaimTypes.Email, user.Email), new Claim("UserId", user.Id.ToString()) };

var jwtResultToCallSubscriptionApi = jwtAuthManager.GenerateTokens(user, claims, DateTime.Now);

var userAllowedActions = await subscriptionServices.RetrieveCurrentUserActiveSubscription(jwtResultToCallSubscriptionApi.AccessToken);

if (!userAllowedActions.IsEmpty()) { claims.Add(new Claim(Common.IdentityConstants.ScopeClaim, string.Join(" ", userAllowedActions))); }

return claims; }

Note
Configuration, plumbing and troubleshooting your software foundation take a considerable amount of time in your product development. Consider using Pellerex which is a complete foundation for your enterprise software products, providing source-included Identity and Payment functions across UI(React), API(.NET), Pipeline(Azure DevOps) and Infrastructure(Kubernetes).

All I need to do to verify an already issued RefreshToken, is to use the VerifyUserTokenAsync method of UserManager and pass the arguments and get a validation response like below:

public async Task<JwtAuthResultViewModel> Refresh(string refreshToken) { var user = await userManager.FindByEmailAsync(userContext.Email);

var isValid = await userManager.VerifyUserTokenAsync(user, appSettings.AppName, appSettings.RefreshTokenName, refreshToken);

if (!isValid) { return null; }

var claims = await GetUserClaims(user); return await jwtAuthManager.GenerateTokens(user, claims, DateTime.UtcNow); }

If the validation failed, this means the user needs to go to Login page again and sign in. Otherwise, we generate a new pair of AccessToken and RefreshToken and return it to the user without the need to Login again.

If you like to read more on the last two methods GetUserClaims, and GenerateTokens, refer to my detailed post on SignIn with React and Asp.NET API.

Also if you have a need to automate the Refresh Token process into your UI, refer to calling secured API in React using Axios.

Summary

In this post I explained the need for Refresh Tokens, and how to use them to issue a new pair of Access Token and Refresh Token, to avoid forcing the user to go through the login process again.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK