I've been thinking about JSON Web Tokens (JWTs) a lot lately and maybe not everything, but with a little creativity, every reason for authenticating stuff I can think of can be done with JWTs.
Together, we'll look at how we can apply JSON Web Token validation to authenticate for multiple schemes such as Two-factor authentication using Passwordless Email and SMS Authentication, Access and Refresh Tokens without making major changes to your architecture.
Also, we'll focus on Pseudo-code rather than code ....
First, what are JSON Web Tokens?
JSON Web Tokens are encrypted strings that contain information that describe a user of a system, and the user's claims to that system.
For example, my NerdHands account JWT could contain claims such as my NerdHands Author ID, the Date the JWT was created, and its Expiry Date.
The browser can then pass the JWT to the NerdHands server to grant me access to my editor.
This article -> here -> does an awesome job of introducing you to JWTs if you still have questions.
So, shall we begin with Access Tokens?
This is perhaps the most popular application of JSON Web Tokens.
It basically goes like this:
- A user performs basic authentication with
password(or some variant of this).
- If successful, a JSON Web Token is generated containing the
UserIdand maybe a role like
- This JWT string will be supplied for subsequent requests to the server.
- When validating the JWT, the server will confirm that it is still valid, by checking its expiry date.
- If valid, the server proceeds with the request, else it bounces the user with an appropriate response.
If a JWT is marked as invalid because it has expired, a new JWT has to be created by re-authenticating the user. This could mean the user needs to sign in again.
But does it have to?
Couldn't we somehow extend the authentication time while still maintaining authentication integrity?
This brings us to:
Access Tokens expire for various reasons, and when they do, we need to bring them to life.
This is done with Refresh Tokens, which are token strings we can pass to an authentication server to retrieve a brand new access token.
Refresh Tokens can also be JWTs with the plot twist (yea) that its role attribute will be something like
"refresh-token" so the server does not confuse it with an Access Token.
So when an Access Token is about to expire, pass the refresh token to an endpoint which:
- Checks that the access-token and refresh-token are still valid
- Checks that the access-token and refresh-token have the same user id claims
- Creates and returns a new access-token JWT
Passwordless Email Authentication
When you want to get into a slack team, you have the option of authenticating via a magic link sent to your Email.
So, how can we use JWTs for this too?
The magic link should contain a ... (say it along with me) ... JWT!!!
- So we create a JWT containing the user's ID, and a role marked as
"mail-token"or some other identifier that shows that the JWT is only to be used for Email Validation.
- The JWT is placed in the query-string of a callback URL and embedded in the sign-in email sent to the user.
- The user clicks on the URL, before rendering the page, the server confirms that the JWT is a
"mail-token", is valid and contains the ID of a valid user.
- If validation is successful, the server creates a new Access Token JWT for the user.
Take a moment to think about the implications of the ease of embedding this feature in your applications.
Passwordless SMS Authentication
This is popular in applications that perform user financial transactions like banking applications and third party apps like QuickTeller and Thrive-send (did I get that spelling right?).
A token made of digits is sent to the user's phone via SMS, and a form appears on the browser for the user to enter the digits sent to their phone within some allotted time (say 5 minutes?).
So how can we use JWTs for this?
The digits in the SMS code can be generated randomly, of course. I'll leave you to the details of that.
Now, encode that SMS code in a JWT also containing a UserId claim.
Be sure to encode the expiry date for the JWT with the future date when the SMS code becomes invalid.
When the user enters the potential SMS code sent to his/her phone, the server can compare it when the SMS code placed in the JWT you created before and validate it.
JWTs carry information about the user's claims, so it's not necessary to maintain such state elsewhere.
This means if you're not currently using JWTs and you want to add them to your site, you don't need to make any changes to your DB structure.
This makes them perfect for stateless systems like REST APIs, and also easy to integrate with systems that keep state like MVC and other traditional session-based web architectures.