1

I built some JWT middleware for my Asp.net Core REST service based on some examples I found online. I get that the response looks like:

{
   "access_token":"...",
   "expires_in":3600,
   "refresh_token":"???",
   "token_type": "Bearer",
}

I understand how to create access_token:

Claim[] claims = new Claim[]
{
    new Claim(JwtRegisteredClaimNames.Sub, strUsername),
    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
    new Claim(JwtRegisteredClaimNames.Iat, dtNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
};

JwtSecurityToken jwtAccess = new JwtSecurityToken(_options.Issuer, _options.Audience, claims, dtNow.DateTime,
                                                  dtNow.DateTime.Add(_options.AccessTokenExpiration), _options.SigningCredentials);

The question is how do I create refresh_token? I have searched high and low and can't find much documentation on it. Basically all every reference says is "its a token stored in a database with a longer TTL that you can create a new access_token from".

So is a refresh_token the same exact thing as access_token with just the longer TTL and the additional step that its validated against the database?

Some of the example JWT responses I've seen seem like the refresh_token is much shorter. My access_token is signed with a certificate using RSA515, so the string is kinda long...

Nkosi
  • 235,767
  • 35
  • 427
  • 472
SledgeHammer
  • 7,338
  • 6
  • 41
  • 86
  • Now personally my refresh tokens are just JWTs with longer TTL and a little more information that help me verify the resource owner. It could be a simple GUID used to map user to token where the expiry time is also stored in the database along with the token. – Nkosi Sep 16 '16 at 03:30
  • Take a look at the following article from Auth0 and it support links https://auth0.com/docs/tokens/refresh_token – Nkosi Sep 16 '16 at 03:45
  • @Nkosi, in that link, the refresh_token is really short. – SledgeHammer Sep 16 '16 at 03:59
  • @Nkosi, I would think a GUID is pretty insecure. – SledgeHammer Sep 16 '16 at 03:59
  • Which is why I usually tend to use another JWT for the refresh token. It may seem redundant. but i store a Guid within the JWT to act as an identifier check the database for that along with some other identifiable information for the client. I would say it is more a matter of preference – Nkosi Sep 16 '16 at 04:01
  • @Nkosi, So, in my case, as I said, I am using a cert to sign with RSA512, so my access_token is 1K. You have a 1K token for the access and the refresh? – SledgeHammer Sep 16 '16 at 04:06
  • Well then in that case a decision would have to be made concerning the length of the payload. I would go with a shorter encoding. It is a matter if preference. the main thing is about making sure that the refresh token is safely stored and not leaked. – Nkosi Sep 16 '16 at 04:07
  • @Nkosi, if I may ask, do you use a cert? Or simple password? to sign your JWT? I was using a simple password before, but I changed it to a cert... signing with a cert may be too cumbersome though due to payload size. – SledgeHammer Sep 16 '16 at 04:09
  • I've used both for various projects depending on requirements and limitations. payload size was an issue with certs so in those cases we used shorter/simpler refresh tokens. – Nkosi Sep 16 '16 at 04:17

1 Answers1

1

Now personally my refresh tokens are just JWTs with longer TTL and a little more information that help me verify the resource owner.

Take a look at the following article from Auth0 and it support links

https://auth0.com/docs/tokens/refresh_token

It could even be a simple GUID used to map user/client to token where the expiry time is also stored in the database along with the token.

The following example is from the link sited above where they use what looks like a Guid for the refresh token.

So, for instance, assuming there is a user 'test' with password 'test' and a client 'testclient' with a client secret 'secret', one could request a new access token/refresh token pair as follows:

$ curl -X POST -H 'Authorization: Basic dGVzdGNsaWVudDpzZWNyZXQ=' -d 'grant_type=password&username=test&password=test' localhost:3000/oauth/token

{
    "token_type":"bearer",
    "access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiVlx1MDAxNcKbwoNUwoonbFPCu8KhwrYiLCJpYXQiOjE0NDQyNjI1NDMsImV4cCI6MTQ0NDI2MjU2M30.MldruS1PvZaRZIJR4legQaauQ3_DYKxxP2rFnD37Ip4",
    "expires_in":20,
    "refresh_token":"fdb8fdbecf1d03ce5e6125c067733c0d51de209c"
}

Once their token has expired they make a call passing the refresh token to get a new access token.

Now we can use the refresh token to get a new access token by hitting the token endpoint like so:

curl -X POST -H 'Authorization: Basic dGVzdGNsaWVudDpzZWNyZXQ=' -d 'refresh_token=fdb8fdbecf1d03ce5e6125c067733c0d51de209c&grant_type=refresh_token' localhost:3000/oauth/token

{
    "token_type":"bearer",
    "access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiVlx1MDAxNcKbwoNUwoonbFPCu8KhwrYiLCJpYXQiOjE0NDQyNjI4NjYsImV4cCI6MTQ0NDI2Mjg4Nn0.Dww7TC-d0teDAgsmKHw7bhF2THNichsE6rVJq9xu_2s",
    "expires_in":20,
    "refresh_token":"7fd15938c823cf58e78019bea2af142f9449696a"
}

Security Considerations

Refresh Tokens are long-lived. This means when a client gets one from a server, this token must be stored securely to keep it from being used by potential attackers, for this reason it is not safe to store them in the browser. If a Refresh Token is leaked, it may be used to obtain new Access Tokens (and access protected resources) until it is either blacklisted or it expires (which may take a long time). Refresh Tokens must be issued to a single authenticated client to prevent use of leaked tokens by other parties. Access Tokens must also be kept secret, but due to its shorter life, security considerations are less critical.

Nkosi
  • 235,767
  • 35
  • 427
  • 472