Auth 101: Token Based Authentication

Shalon N. NgigiShalon N. Ngigi
8 min read
💡
This is a stack agnostic strategy that can be implemented in a number of frameworks and languages. I, however, might reference ReactJS (client) and NodeJS (server).

Overview

There are a number of ways to implement secure authentication and authorization (some strategies are usually combined depending on application needs):

  • Token based authentication

  • OAuth using Single Sign-On (SSO) or trusted 3rd party identity providers like Google and Microsoft

  • Session based authentication - server managed sessions

  • Certificate and hardware based authentication using devices like YubiKeys and smart cards

This article goes over the token based authentication strategy, where a typical authentication flow looks like:

  1. Client presents a login form to a user, user fills in their crendentials.

  2. Client sends these credentials to the server.

  3. Server verifies credentials, generates a token and sends the token back to the client.

  4. Client stores the token and adds the token to subsequent requests to protected endpoints on the server

  5. Server verifies the token validity and executes the request.

A sequence diagram illustrating a JWT authentication process. It includes a user, client, server, database, and a third-party JWT generator. The process shows user login, token generation and storage, request for books with JWT, token verification, and displaying books to the user.

My AQs

Before we continue, I had some questions in mind (that you might have too). I’ll try to answer them in the article 😅

  1. How do we send the initial credentials to the server securely? Wouldn’t a snooper look at the network request and use the credentials to generate their own token? only send data over https, add Multi-Factor Authentication.

  2. How do we store passwords securely in the database? hash and salt passwords before storing them in the database.

  3. What is a JWT token? It is a medium used to transmit data between 2 parties that can be verified using digital signatures

  4. How long should a token live? depends on the app needs, but always set expiry time.

  5. How should we store tokens on the client? use in memory storage with a token refreshing strategy

  6. What is a refresh token? a token that lets us renew our expired tokens.

  7. What are some security vulnerabilities around token authentication? xss and csrf attacks, man-in-the middle attacks, non-expiring tokens, unsanitized data.

  8. How do we avoid bad UX with expiring tokens? Use silent authentication techniques.

Token based authentication

JWT Standard

JSON Web Token (JWT) is an open standard that defines a JWT Token as a compact safe medium of transferring data between two parties where the data is presented in a JSON object that is encoded and digitally signed.

A JWT is made up of 3 encoded sections:

  1. Header: contains metadata about the token, it tells us the token type and the algorithm used to sign the token

  2. Payload: contains the claims (information about the entity in question and additional data). Claims can be:

    • Registered: exp (expiration time), sub (subject)

    • Public: information we want to transmit e.g. name, roles

Sensitive data should not be included in the payload unless additional encryption is applied
  1. Signature: used to verify token integrity and authenticity. A signature is created using the algorithm specified in the header to sign a combination of the encoded header, the encoded payload and a secret. The secret helps us verify that the token hasn’t been tampered with and should be kept somewhere safe (use env variables)

Here is a decoded token structure from https://jwt.io/

Authentication flow in detail

Step 1: Sign up a user

On the client:

  • Use a form to collect user details

  • Validate and sanitize the data on the frontend (some checks include checking email format, empty strings, password strength)

  • Consolidate the data in a JSON object and send it to a signup endpoint on the server.

On the backend, when we receive data:

  • Validate and sanitize the data

  • hash and salt the password using a library like bcrypt - we should never store passwords as plain texts

  • check for user duplicates if needed and store user details in the database

💡
Reduce the steps needed to authenticate by redirecting a user to the app after signup instead of the login page. We already have the user credentials from the signup, we can return the access token on signup just like we would on login.

Step 2: Login a user

On the client:

  • Collect user credentials, sanitize the data if needed (some checks include checking email format, empty strings).

  • Consolidate the data in a JSON object and send it to a login endpoint on the backend server.

On the backend, when we receive data:

  • Verify user exists in database.

  • Verify the password hash stored in the database matches the password provided.

Step 3: Generate tokens

Using a jwt library generate two JWT tokens, an access token and a refresh token.

  • Access token: should be short lived, with expiry set to a few minutes or hours. This token contains the sensitive information the frontend client needs to know e.g. user id, user roles or permissions.

  • Refresh token: is a longer lived token, with expiry set to a few days or months. This token is used to generate new access tokens when the current ones expire.

Step 4: Store the tokens

The access token:

  • Server sends back the access token to the client as a JSON response

  • Client stores the token in memory, in React we can use state, context or memory.

    🛑
    Storing access tokens in memory is safer than local storage. Tokens in memory are vulnerable to XSS attacks if the application has JavaScript injection vulnerabilities
  • Client then includes the token in the Authorization header for every subsequent request to protected endpoints.

Store the refresh token in a HTTP-only cookie:

  • Server sets the cookie in the response header

  • Client receives the token and stores the cookie in the browser's cookie storage.

  • The cookie is then automatically included in every subsequent request to the server.

A HTTP-only cookie is a type of cookie designed to be accessed only by the server via HTTP requests.

It is inaccessible to client-side javascript scripts which mitigates against xss attacks.

If we set the right security configurations (e.g. setting same origin) we can mitigate against csrf attacks. Additionally, CSRF tokens or SameSite cookies can protect against CSRF.

The cookie persists between browser sessions for the same browser profile. Opening an incognito tab or a new browser profile will need a new auth session.

In summary, here are some differences between the 2 tokens

Access TokenRefresh Token
PurposeUsed to transmit sensitive information between client and serverUsed to obtain new access tokens when the current one expires
LifespanShort lived (minutes to hours)Long lived (days to months)
How it is usedSent with each request to protected resources (in the Authorization header)Only sent to a specific endpoint when we need to generate new tokens
Where it is storedStored on the client in short term storage e.g. memory, state or contextStored in a HTTP-only cookie
Data storedSensitive data: user id, roles, permissions, name etc.Minimal data: user id, token id
Security riskLimits misuse because it expires quickly, but contains sensitive infoMore sensitive because new tokens can be generated if exposed

Step 5: Using the token

The access token:

  • Client sets the token in the Authorization header of the request to the server.

  • Server gets the token from the header, verifies the signature and expiry, decodes the token, validates expiry, verifies user permissions and then executes the request.

The refresh token:

  • Client makes a request to a refresh token endpoint.

  • Server obtains the refresh token from the HTTP-only cookie, verifies the signature and expiry, and decodes the token.

  • Server then uses the refresh token info to generate a new access token which is returned to the client in the JSON response (we can go a step further and blacklist the older refresh token so that it isn’t re-used).

  • Client stores new access token and the req/res cycle continues.

💡
Passport.js has a fantastic library that works with most frameworks and makes auth flow very easy 🙂

How can we avoid bad UX if the token expires too quickly?

Use silent authentication techniques:

  • We could explicitly lookout for a token expiry error on the client, and make a refresh token request, then use the token returned to retry the failed request. All this could be gracefully handled so the user never knows.
  • Or we can keep track of token expiry on the client so that we can make pre-emptive refresh requests. But this strategy could introduce performance issues.

In summary:

On sign up or login, generate 2 tokens, a short-lived access token and a longer-lived refresh token.

Use the access token to transmit the needed data (e.g. user id, roles, permissions etc.) between client and server. Store the token in client temporary storage like memory or state. Send it via the Authorization header for protected endpoints.

Use the refresh token to generate new access tokens on expiration through a refresh token endpoint. Store the token in a http-only cookie.

Secure best practices

  • Sanitize and validate input data in all relevant application layers.

  • Hash and salt passwords before storing them in the database.

  • Implement Role Based Access Control (RBAC).

  • Send requests via https to avoid man-in-the-middle attacks and network eavesdropping (consider using TLS certificates).

  • Implement Content Security Policy (CSP) to prevent certain attacks such as Cross-Site Scripting (XSS) and data injection attacks.

  • Implement 2FA or MFA (Multi-Factor Authentication).

  • Use OAuth2 providers like Google and Microsoft as identity providers, they have compliant libraries that make auth easier. OAuth builds on top of the token based strategy.


So, did I answer all (y)our token authentication based questions?

12
Subscribe to my newsletter

Read articles from Shalon N. Ngigi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shalon N. Ngigi
Shalon N. Ngigi