DEV Community

RaphaelDiasc21
RaphaelDiasc21

Posted on

Access token and Refresh token, approach with golang

Why login again if you have something that I trust ?

When we are dealing with authentication and authorizations in web services, the standard approach is to work with JWT tokens.

A quick recap about JWT (JSON Web tokens) is that it basically a secure form to dealing with information exchange and authentication requests between services, having data stored in a JSON format.

However despite the efficiency of the JWT's, it's a good practice to give these tokens a valid time to live, and after this time is over, the token is treated as expired. A normal behaviour when this situation happens is to force the user to login again in the application to get a new token. But to avoid this incovenient step and make it clear as possible to the users, a common approach is to "refresh" the old token for a new one, using something that the application is able to trust. To use as a trustful artifact for the application's backend, we can use another (guess what...) JWT TOKEN.

So, to resume the concert, when the user does the login in the application, the backend responds with two informations, one is the access-token (standard nomenclature) a JWT token that will be used to authorize the user on the web services of the app, and the second info is the refresh-token (standard nomenclature) a JWT token that will be used to give the user another new token when the actual access-token would expired. Bellow a image of a request and a response

Image description

Proving the authenticity of the access-token (check the payload section, the user's email is the same used to singIn and it's passed the user's id as sub property)

Image description

Proving the authenticity of the refresh-token (check the payload section that we only have the user's id as sub)

Image description

It is important to notice the usage of the audience (aud) and issuer (iss) claims, because these two informations will be requested on the logic to refresh the token

The GOLANG code that makes all the magic

Libraries used
bcrypt: https://pkg.go.dev/golang.org/x/crypto/bcrypt
golang-jwt: https://github.com/golang-jwt/jwt
Gin (Http framework): https://github.com/gin-gonic/gin

First of all, on this example I wanted to emphasize the implementation of the refresh token and JWT dealing work using the Go language, abstracting the architectural good practices and breaking all the pieces into functions

The signIn endpoint

Image description

In the first part of the code (until line 85) I just parse the request, take the credendials sent by the user, and validate all the information checking if the email provided exists on the database and the password is correct. Bellow a detailed image from the FindAndValidateUserCredendials

Image description
So, basically what the code does is query the user using the email information provided as a filter, if the user exists is proceed to check if the hashing password stored for this user on the database, is matched with the plain text one provided in request. I used the crypto bcrypt library in golang to deal with the hashed password

After the credentials is validaded the JWT tokens is created

Image description

Image description

The access token is generated with claims that represents the time for the token to be expired (exp), on the example I just leave 1 minute for token to be valid, but the standard is 24 hours, the issuer (iss), that says who issued the token, the audience (aud), that identifies the recipients that the JWT is intended for, and some user info like the email (user_email) and the subject (sub), that represents the user's id. To deal with all JWT work I used the jwt-golang library, so these methods like jwt.New, SignedString are all from the lib.

Notice that the secret for secure (encrypt) the JWT token I'm just using a simple word - secret, that is not a best practice. In real world you should hash your secret using some crypto algorithm and store it into a file that no one can find easily, SOO... Take the Tip

Image description

And to finish the refresh token is generated basically using the same idea from the access-token, taking the difference that the expiration time (exp) is for 30 days and only user info used is user's id, represented on the subject (sub).

After that I returned the response for the user with the access-token and refresh-token. Check bellow:

Image description

refresh-token endpoint

So, when the actual access-token used by the user is expired, the frontend will be notificated (receiving a 401 status code from some guarded endpoint) and will be calling the refresh-token endpoint to get another valid token. Checking the endpoint's code bellow

Image description

Until the line 111 I just validate the refresh token sent in the request and get the user info using the user's id sent also in the request. This user info will be used to generate another access-token. Bellow is the _ValidateToken _function on detail

Image description

Basically with the help of golang-jwt library the code parses the JWT token received on the function's argument and store all the claims from the parsed token into RefreshTokenClaims struct also received on the function's arguments. This validation is about the signature key used in the token, proving if the token was generated using the same private key of my application or not.

And after the token key signature is validated, the audience claim and the issuer claim is checked to attempt if these are from the same issuer and audience, that the application expected to be. The issuer claim must be from the application who issued the token (in this case is my application) and the audience must be equals the audience that was designated decided by me when developed the system. With these validations the token is proved to be the same generated from my application and then I can TRUST in it. Generating new tokens for the user.

Notice that is generated a new access-token and a new refresh-token, that is because the new refresh-token would be used to refresh again the token, repeating all the proccess. And in the code I did not sent a error for the user in the case the audience or issuer not match the criterias, but in a real world application it would be done.

The result is new tokens generated:

Image description

Bellow all the structs used in the code

Image description

Oldest comments (0)