loading...
Cover image for API Authentication Workflow with JWT and Refresh Tokens

API Authentication Workflow with JWT and Refresh Tokens

gokayokyay profile image Gökay Okyay ・3 min read

Hey everyone, this is my first post so go easy on me :P

So I want this post to help anyone who wants to build an authentication system. I'm sharing a workflow, not the implementation, so that you can change the implementation in the way of your needs.

I'll add some scenarios throughout the post and will move on to server-side and lastly client-side. I'll assume that you already have a registration system.

Scenario Part 1

User registered. Okay, then we need to send a jwt and a refresh token. So server creates it and sends them back to the user, user's browser saves tokens then our cute little user is free to roam around in our app. So what happened actually?

Server-Side

  • Client registered
  • Created a short-lived JWT and a refresh token for the specified user
  • Save the refresh token in a DB, it can be a Key-Value DB like Redis.
  • Send JWT back to client, and add the refresh token to client's Cookie Storage with HttpOnly and Secure flags.

You can set cookie in node like this:

response.setHeader('Set-Cookie', 'foo=bar; HttpOnly');

Client-Side

  • Hit server's registration endpoint.
  • Save JWT to localStorage.

As a note: Local storage is vulnerable to XSS attacks so becareful :)

Okay that wasn't that much. But a couple of questions can be arised with our little note. Here's the most specific one: Why did we save JWT to localStorage if it is vulnerable?

The Answer

We have saved JWT to client's local storage because you might have noticed, our JWT is short-lived, say 30 minutes. This way we can add JWT to Authorization header of our API requests. (The bearer thingy)

And then we have created another token called refresh token, this can anything, your pet's name reversed with a counter or some random numbers... anything! I prefer a node package called "uuid". We saved the refresh token to client's cookie storage with httponly and secure flags. This means that this particular cookie won't be accessible by javascript. And the secure flag is about https, you can understand what it does :P

This way when user hits our API, our server can validate the JWT and refresh token of the user.

Scenario Part 2

User never gets bored in our app! He constantly refreshes the feed and wait for new things, but guess what our little user? 30 minutes have passed already! So your JWT is EXPIRED. Now what?

This time I'll explain it like a sequence diagram.

Client-Side

  • User makes an API request with expired JWT.

Server-side

  • API request is received, check the JWT and refresh token. BOOM JWT is expired, send unauthorized response to client (401).

Client-Side

  • Received unauthorized response from the previous API request.
  • Hit refresh endpoint of the API.

Server-side

  • Received the expired JWT checked it and refresh token is assigned to current user. Now refresh the JWT and send it back to user.

Client-side

  • Received JWT, saved it to the localStorage again.
  • Repeat the failed API request.
  • Continue to operate.

In node's jsonwebtoken package, there's an option while verifying the jwt. It is ignoreExpiration. You can check if the token is modified.

Scenario Part 3

So our user is sleepy, he wants to logout. But it's not that he got bored, he just wants to sleep :). He clicked the logout button. What happens now?

Client-Side

  • Clear the localStorage or just remove the jwt.
  • Make an API logout request.

Server-Side

  • Received logout request. Delete refresh token from the DB (like redis :P)
  • Set refresh token cookie's expiration date to any date in past.

Poof our user is logged out.

Such a long post for the first time eh? I might add more information if posts can be edited :P.

Important note: Mobile authentication should be different than this. I will share a post about it in future.

See you in another post!

Posted on by:

gokayokyay profile

Gökay Okyay

@gokayokyay

A developer who loves to code with JS && Swift. Tries to re-create cool open source projects in his spare time.

Discussion

markdown guide
 

In second scenario you wrote that when server receive expired token, it sends unauthorized response to client.
Client recieve unauthorized response and hit refresh endpoint.
My question: where should I store logic for my refresh endpoint? Do I need second server for that?
I have a few suggestion and I don't know which is better.

 

Hey! Sorry for the late reply.
It depends on the architecture for your web app. If you use microservices architecture, you could possible create another service for the token refreshing service and put the logic in there.

 

How mobile authentication should be different?

 

Hey! Sorry for the late reply.
Actually it's very similar but the refresh token part. In mobile apps, the API's that mobile OS provides us differ, therefore, we can change the structure of our JWT and refresh token. For example if your app is doing something critical, you'll probably want to make your JWTs short-lived, say 5 minutes and make your refresh token a JWT (which will live longer). Or if your app is not doing something critical, you may want your JWT to live longer than 30 minutes. But the idea is the same :)

 

I have a question, Isn't refresh token supposed to be another jwt token with longer expiry time?, In that case how would you update the redis db that has refresh jwt after it expires

 

Hey, it's a very good point!
The refresh tokens can be very long-lived JWTs but in this case you'll want to handle refresh token creation/expiration differently than I've mentioned above. Maybe you can create a logic something like that users need to post their expired refresh tokens with jwts. Than you check them both and verify that they belong to the same user and create another refresh token and jwt. Hopefully I could answer your question :)

 

thank you for your response,

1.store refreshToken=>token in redis db in server (if we can store in redis server will that cache remain alive till the refresh_token remains alive -like for 2-3 days or even a week)

2.whenever server recieves an expired token, it verfies the expired token from that mapping in no.1 and sends a new token to the client

3.I can store refreshToken in a cookie with better security like {sameSite} {httpOnly} {secured} and send it to server in every request from client

please correct me if this understanding is wrong, thank you