Open In App

Access Token vs Refresh Token: A Breakdown

Last Updated : 27 Sep, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

OAuth 2.0 and OpenID Connect (OIDC) use tokens instead of traditional usernames and passwords to grant access to secure resources. This makes the login process easier and more secure.

There are two main types of tokens in OAuth: access token and refresh Token. Access tokens are used to access resources, while refresh tokens are used to get new access tokens when the old ones expire. Both access and refresh tokens often use a format called JSON Web Token(JWT). JWTs are compact, and self-contained, and have become the standard for securely sharing authentication information across different platforms.

Access-Token-vs-Refresh-Token-A-Breakdown

These are the following topics that we are going to discuss:

What is an access token?

An access token is like a digital key, often in the form of a JWT, that lets users access resources without needing to log in repeatedly.

  • A user wants to access resources through a client app.
  • The user gives permission for the app to get an access token from an authorization server.
  • The authorization server sends an access token to the app.
  • The app checks the token and, if valid, allows the user to access the resources.

Access tokens usually have a very short lifespan: in many cases, they last only a few hours. For example, the access token lifespan in Microsoft's identity platform is between 30-90 minutes by default. These are also variable, assigned randomly to a value in the range.

What is a Refresh Token?

A refresh token is a special token that extends the life of an access token. When an access token expires, a refresh token can be used to get a new one without making the user log in again. Refresh tokens are usually stored securely on the authorization server.

They work together with access tokens to allow longer sessions, improving user experience and security. Unlike access tokens, refresh tokens last much longer-for example, up to 90 days in some systems. While not always necessary, they make managing sessions easier and more secure.

When to Use Each Type of Token

  • Access Tokens: Access tokens are great if you want a passwordless login for your software. They work best when users need to access shared resources, like when someone needs to view or edit files owned by someone else. Access tokens make this process easier and more secure.
  • Refresh Tokens: When you use access tokens, it's usually a good idea to also use refresh tokens, especially if users are likely to stay logged in for a long time without refresh tokens, users would have log in again frequently, which can be annoying. Refresh Tokens the session going smoothly without compromising security.
  • When to skip Refresh Tokens: There are times when long-term access isn't necessary. In these cases, you might not need to use refresh tokens at all.

Tokens vs. Cookies

Tokens are typically used in APIs for authentication and authorization purposes. The most common type of token is a JWT (JSON Web Token). Tokens are passed between the client and server in request headers (usually Authorization: Bearer). Cookies are stored on the client side (browser) and can be sent automatically with HTTP requests. Cookies are often used for session management, but they can also store authentication tokens like JWTs.

  • Storage: Tokens are typically stored in local storage or session storage, while cookies are stored by the browser.
  • Transmission: Cookies are automatically sent by the browser to the server; tokens need to be manually added to the request header.
  • Security: Cookies are vulnerable to CSRF attacks, while tokens are vulnerable to XSS if stored improperly.

Refresh Token Usage

Refresh tokens are long-lived tokens used to get new access tokens without requiring the user to log in again. They are typically stored securely (e.g., in HTTP-only cookies or in secure storage).

  • User authenticates and receives an access token (short-lived) and a refresh token (long-lived).
  • When the access token expires, the refresh token is used to obtain a new access token without asking for user credentials.
  • This allows the user to stay logged in for longer periods.

Refresh Token Limits

  • Security: Refresh tokens should be stored securely to avoid misuse. If compromised, they can be used to obtain new access tokens.
  • Expiration: While refresh tokens generally have longer lifetimes than access tokens, they should still expire or be invalidated after a certain period for security.
  • Rotation: Refresh token rotation is a security technique in which a new refresh token is issued every time the old one is used, making the previous one invalid. This limits the damage if a refresh token is compromised.
  • Limitations:
    • Revoke policy: The server should be able to invalidate refresh tokens (e.g., when a user logs out).
    • Concurrent refresh: Limiting multiple refresh token requests from different devices can enhance security.
Access-Token-vs-Refresh-Token-01

Access Tokens

Access tokens are short-lived tokens that clients use to access protected resources (APIs). They are typically passed via the Authorization header.

Properties:

  • Expiration: Access tokens have a short lifetime (e.g., minutes or hours) to limit exposure in case of compromise.
  • Stateless: They do not require server-side session storage since all information is encoded in the token.

Usage:

  • The client uses the access token to authenticate API requests until the token expires.
  • After expiration, a new access token must be requested using the refresh token.

JWT Validation

JSON Web Tokens (JWT) contain three parts: header, payload, and signature.

  • Header: Contains metadata, including the signing algorithm.
  • Payload: Contains the claims (information about the user).
  • Signature: Created by signing the header and payload with a secret or private key.

Validation:

  • Verify the signature to ensure the token hasn’t been tampered with.
  • Check the expiration (exp) and not-before (nbf) claims to ensure the token is still valid.
  • Optionally, check the audience (aud) and issuer (iss) claims to confirm the token is intended for the correct resource.

Signing Algorithms

  • HS256: HMAC using SHA-256. A symmetric algorithm where the same secret key is used to both sign and verify the token. Easier to implement but less secure if the key is exposed.
  • RS256: RSA using SHA-256. An asymmetric algorithm where the token is signed with a private key and verified with a public key. It’s more secure as public keys can be freely distributed, but private keys remain secret.
  • ES256: ECDSA using P-256 and SHA-256. Elliptic curve-based signing, providing similar security to RSA with smaller key sizes and signatures.

Signing Keys

  • Symmetric Keys: Used in algorithms like HS256. The same key is used for both signing and verification, so the key must be kept secret. If the key is compromised, anyone can generate valid tokens.
  • Asymmetric Keys: Used in algorithms like RS256. The signing key (private key) is kept secret, while the verification key (public key) can be shared. This provides better security, especially in distributed systems where multiple services need to verify tokens.

Example: This code sets up a simple Express server with user authentication using JWT tokens, including login and refresh token functionality with cookies for token management

JavaScript
const dotenv = require('dotenv');
const express = require('express');
const cookieparser = require('cookie-parser');
const jwt = require('jsonwebtoken')
const bodyParser = require('body-parser');

// Configuring dotenv
dotenv.config();
const app = express();

// Setting up middlewares to parse request body and cookies
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieparser());

const userCredentials = {
    username: 'admin',
    password: 'admin123',
    email: '[email protected]'
}

app.post('/login', (req, res) => {
    // Destructuring username & password from body
    const { username, password } = req.body;

    // Checking if credentials match
    if (username === userCredentials.username &&
        password === userCredentials.password) {

        //creating a access token
        const accessToken = jwt.sign({
            username: userCredentials.username,
            email: userCredentials.email
        }, process.env.ACCESS_TOKEN_SECRET, {
            expiresIn: '10m'
        });
        // Creating refresh token not that expiry of refresh 
        //token is greater than the access token

        const refreshToken = jwt.sign({
            username: userCredentials.username,
        }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '1d' });

        // Assigning refresh token in http-only cookie 
        res.cookie('jwt', refreshToken, {
            httpOnly: true,
            sameSite: 'None', secure: true,
            maxAge: 24 * 60 * 60 * 1000
        });
        return res.json({ accessToken });
    }
    else {
        // Return unauthorized error if credentials don't match
        return res.status(406).json({
            message: 'Invalid credentials'
        });
    }
})

app.post('/refresh', (req, res) => {
    if (req.cookies?.jwt) {

        // Destructuring refreshToken from cookie
        const refreshToken = req.cookies.jwt;

        // Verifying refresh token
        jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET,
            (err, decoded) => {
                if (err) {

                    // Wrong Refesh Token
                    return res.status(406).json({ message: 'Unauthorized' });
                }
                else {
                    // Correct token we send a new access token
                    const accessToken = jwt.sign({
                        username: userCredentials.username,
                        email: userCredentials.email
                    }, process.env.ACCESS_TOKEN_SECRET, {
                        expiresIn: '10m'
                    });
                    return res.json({ accessToken });
                }
            })
    } else {
        return res.status(406).json({ message: 'Unauthorized' });
    }
})

app.get('/', ((req, res) => {
    res.send("Server");
    console.log("server running");
}))

app.listen(8000, () => {
    console.log(`Server active on http://localhost:${8000}!`);
})

Comparing Access Token and Refresh Tokens

Access tokens and refresh tokens work together to manage user access. Access token give users access to resources, while refresh tokens help keep the session going without needing to log in again. They aren't separate they works as a team. The access token gives initial access, and refresh token extends that access over time, making the login process smoother and more secure for the user.

Properties

Access Token

Refresh Token

User Interface

Authenticate and authorize user up-front

Users are re-authorized without re-authenticating

Transmission and storage

Tokens are send over secure server (HTTPS) and then store on client server.

Tokens are send over HTTPS, like access token but store on authorization server

Security considerations

Short lifespan and the ability to revoke access power strong security

Longer lifespan are offset by less frequent refreshes and grater revocation ability


Next Article

Similar Reads