How to manage with Refresh Token in ASP.NET Core 2.0 Web API

in #utopian-io7 years ago (edited)

What Will I Learn?

  • Using JSON Web Token
  • Manage Refresh Token
  • Create professional authorization in WEB API project

Requirements

  • C#
  • .NET Core 2.0
  • Visual Studio 2015+/Visual Studio Core
  • knowledge from my previous post about JWT basic

Difficulty

  • Intermediate

Tutorial Contents

This is continuation of my previous post where i showed you how to create JWT, how to add data to appsetting.json. In this tutorial I will show You how to manage refresh Token, so how to create 2 steps authorization.

What are access and refresh token?

  • access token is JWT Token which have very short expiry date, this is important because you can use this token for only few minutes. After this time it will expire and you have to get new one. When someone will steal your Token he will be able to use it just for a few minutes. It makes your application much more safe.

  • refresh token in long term token, is it used to create new access token.

Implementation

I will be working in the project from previous tutorial, I will add some necessary classes and functions.
Let's start.

At the beginning we will add to appsettings.json one more definition about expiration our tokens:

"Tokens": {
    "Key": "TokensSecretKey,
    "AccessExpireMinutes": "5",
    "RefreshExpireMinutes": "1000"
  },

As you can see I set 5 minutes long for access token and 1000 minutes for refresh token.

Now we will create some resources for our tokens:

image.png

  • TokenResource
    public class TokenResource
    {
        public string Token { get; set; }
        public long Expiry { get; set; }
    }
  • AuthorizationTokensResource
    public class AuthorizationTokensResource
    {
        public TokenResource AccessToken { get; set; }
        public TokenResource RefreshToken { get; set; }
    }

If we have created two classes with resources we have to change our JwtHandler. Because we have 2 different tokens, we have to create two methods for these tokens.

  • Create Access Token method:
public TokenResource CreateAccessToken(Guid userId, string email, Role role)
        {
            var now = DateTime.UtcNow;
            var claims = new Claim[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
                new Claim(JwtRegisteredClaimNames.UniqueName, userId.ToString()),
                new Claim(ClaimTypes.Email, email),
                new Claim(ClaimTypes.Role, role.ToString()),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.Iat, now.ToTimeStamp().ToString(), ClaimValueTypes.Integer64),
            };

            var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Tokens:Key"])),
                SecurityAlgorithms.HmacSha256);
            var expiry = now.AddMinutes(double.Parse(_configuration["Tokens:AccessExpireMinutes"]));
            var jwt = CreateSecurityToken(claims, now, expiry, signingCredentials);
            var token = new JwtSecurityTokenHandler().WriteToken(jwt);

            return CreateTokenResource(token, expiry.ToTimeStamp());
        }
  • Create Refresh Token method:
public TokenResource CreateRefreshToken(Guid userId)
        {
            var now = DateTime.UtcNow;
            var claims = new Claim[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
                new Claim(JwtRegisteredClaimNames.UniqueName, userId.ToString()),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.Iat, now.ToTimeStamp().ToString(), ClaimValueTypes.Integer64),
            };

            var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Tokens:Key"])),
                SecurityAlgorithms.HmacSha256);
            var expiry = now.AddMinutes(double.Parse(_configuration["Tokens:RefreshExpireMinutes"]));
            var jwt = CreateSecurityToken(claims, now, expiry, signingCredentials);
            var token = new JwtSecurityTokenHandler().WriteToken(jwt);

            return CreateTokenResource(token, expiry.ToTimeStamp());
        }

As you can see, in refresh token we don't have defined role of user and his email. This token has only basic information, only those what are necessary for match user with token.

Now we will create method for refreshing our token. The flow of application should be like this:

  • ask for tokens -e.g login to application
  • return access and refresh token to user after successful login
  • user send reguest for some resource from server, he has to put in header his access token
  • server verify access token and if everything is ok, return 200
  • user again send request to server
  • server verify that access token is expired then return - unatorizes status
  • user send to server his resfesh token
  • server verify user's refresh token and if everything is ok, return new access token and updateg refresh token

method for refreshing token:

public async Task<TokenResource> RefreshToken(Guid userId, string refreshToken)
        {
            var user = await _unitOfWork.Users.GetAsync(userId);
            if (user == null)
            {
                throw new ServiceExceptions(ErrorCodes.InvalidRefreshToken, "Invalid refresh token");
            }

            var tokenHash = _encrypter.GetHash(refreshToken, user.Salt);
            var session = user.Sessions.FirstOrDefault(s => s.Token == tokenHash);
            if (session == null || session.IsExpired())
            {
                throw new ServiceExceptions(ErrorCodes.InvalidRefreshToken, "invalid refresh token");
            }
            session.Refresh();
            await _unitOfWork.CompleteAsync();

            return _jwtHandler.CreateAccessToken(user.Id, user.Email, user.Role);
        }

Now we just have to create one more method in our controller for refreshing token:

[HttpPost]
        [Authorize]
        [Route("account/refreshToken")]
        public async Task<IActionResult> RefreshToken([FromBody]RefreshTokenResource resource)
        {
            var userId = new Guid(User.Identity.Name);
            var accesToken = await _accountService.RefreshToken(userId, resource.RefreshToken);
            return Ok(accesToken);
        }

Thank you for attention, if you want to learn more about ASP.NET Core Web API you can check my profile, or section below -> Curriculum

Curriculum



Posted on Utopian.io - Rewarding Open Source Contributors

Sort:  

Thank you for the contribution. It has been approved.

You can contact us on Discord.
[utopian-moderator]

Hey @babelek I am @utopian-io. I have just upvoted you!

Achievements

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • Seems like you contribute quite often. AMAZING!

Suggestions

  • Contribute more often to get higher and higher rewards. I wish to see you often!
  • Work on your followers to increase the votes/rewards. I follow what humans do and my vote is mainly based on that. Good luck!

Get Noticed!

  • Did you know project owners can manually vote with their own voting power or by voting power delegated to their projects? Ask the project owner to review your contributions!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x