Skip to main content

Token Refresh

Token refresh is an advanced piece of functionality that was included in a recent update to our introductory REST APIs with Flask and Python course. If you've taken that course but not seen this before, do check it out before continuing. The videos provide an even more in-depth explanation!

In essence, token refresh just means storing one more piece of data in the JWT that we give our users.

As well as their id, expiry date, and so on, we can also store whether the token is fresh.

A fresh token is one that was generated just recently, when the user logged in. Remember for a user to log in they need to give us a valid username and password combination. This means a fresh token verifies that a user's username and password was recently used correctly.

This gives us the most confidence that they are who they say they are.

So, what's the alternative?

The alternative is a non-fresh token, or a refreshed token.

JWTs expire after a certain amount of time—normally from a few minutes to a few hours. Normally this means that the user has to log in again, continuously providing their username and password.

That can be really cumbersome (imagine being asked to log in every 10 minutes!).

So instead what we can do is say: "the user logged in before, so as long as we're not doing anything terribly sensitive, let's assume the user can continue to be logged in".

We use token refresh for this. When their JWT is about to expire, we can request a new one by using another token called a refresh token. The resulting JWT is "non-fresh", so we know if was generated using this method, and therefore the user may have been logged in for a long time (we don't know how long for).

This is how we implement token refresh:

class TokenRefresh(Resource):
@jwt_refresh_token_required
def post(self):
current_user = get_jwt_identity()
new_token = create_access_token(identity=current_user, fresh=False)
return {"access_token": new_token}, 200

We take in the refresh token and use it to get the user ID. Then we create a new access token (making sure fresh=False), and give that back to the user. Notice that we never change the refresh token–we only do that when the user logs in again.

Doing something terribly sensitive

When we do want to do something terribly sensitive, we can ask the user to log in again. That way we make sure they are genuine, and they haven't forgotten to log out somewhere (or that nobody has stolen their current access token!).

When we ask the user to log in again, we now get a fresh access token, which as mentioned before is more secure.

To request a new log in, all we need to do in our API is make sure we request a fresh access token.

If we need a fresh access token but the client (mobile or web app) provides a non-fresh one, we will return a 401 UNAUTHORIZED response. Usually clients will then ask the user to log in when they encounter such a response.

After logging in, they can try again and the token will be fresh, so the request will succeed.

This is how we can request a fresh access token in any one of our "sensitive" endpoints:

@fresh_jwt_required
def post(self, name):
# ...
pass

Remember other than the freshness, the data included in a fresh vs. a non-fresh access token is the same. It's just that a fresh one means the user just logged in, whereas a non-fresh one means the user may have been logged in for a long time.

For another write-up on this, check out our blog post.