Skip to main content

Bearer Authentication

Bearer authentication is a mechanism for token-based authentication using the HTTP Authorization header.

What is Bearer Authentication

Bearer authentication is the standard method for sending OAuth 2.0 access tokens in HTTP requests, defined in RFC 6750.

Basic Mechanism

  1. The client obtains an access token from the authentication server.
  2. For subsequent API requests, the client sends the token in the Authorization header.
  3. The server verifies the token and processes the request.
GET /api/users/me HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Origin of the Name "Bearer"

"Bearer" means "holder" or "possessor". It is based on the concept that the person who holds (=Bearer) the token can exercise the rights associated with that token.

Token Formats

JWT (JSON Web Token)

JWT is the most commonly used format for Bearer tokens.

JWT Structure:

Header.Payload.Signature

Example:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Decoded:

Header:

{
"alg": "HS256",
"typ": "JWT"
}

Payload:

{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622
}

Opaque Tokens

Unlike JWT, these tokens do not contain information themselves and only have meaning on the server side.

d8f7a3b2c1e9f4d6a5b8c7e2f1a9d3b4
  • Managed in the database as a token ID or session ID.
  • To know the content of the token, an inquiry to the authorization server is required.

Implementation

Client-Side Implementation

JavaScript (Fetch API)

async function fetchUserProfile(accessToken) {
const response = await fetch('https://api.example.com/users/me', {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
});

if (!response.ok) {
throw new Error('Failed to fetch user profile');
}

return await response.json();
}

Example using Axios

import axios from 'axios';

const api = axios.create({
baseURL: 'https://api.example.com',
});

// Automatically attach token with interceptor
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);

// Auto-refresh on 401 error
api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;

if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;

try {
const refreshToken = localStorage.getItem('refreshToken');
const response = await axios.post(
'https://auth.example.com/token',
{
grant_type: 'refresh_token',
refresh_token: refreshToken,
}
);

const { access_token } = response.data;
localStorage.setItem('accessToken', access_token);

originalRequest.headers.Authorization = `Bearer ${access_token}`;
return api(originalRequest);
} catch (refreshError) {
// Logout on refresh failure
localStorage.clear();
window.location.href = '/login';
return Promise.reject(refreshError);
}
}

return Promise.reject(error);
}
);

Server-Side Implementation

Node.js + Express + JWT

const express = require('express');
const jwt = require('jsonwebtoken');

const app = express();
const SECRET_KEY = process.env.JWT_SECRET;

// Authentication Middleware
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // "Bearer TOKEN"

if (!token) {
return res.status(401).json({ error: 'Access token required' });
}

jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid or expired token' });
}

req.user = user;
next();
});
}

// Protected Endpoint
app.get('/api/users/me', authenticateToken, (req, res) => {
res.json({
id: req.user.sub,
name: req.user.name,
email: req.user.email,
});
});

// Token Issuance Endpoint
app.post('/auth/login', async (req, res) => {
const { username, password } = req.body;

// User authentication (verify with database in reality)
const user = await authenticateUser(username, password);

if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}

// Generate Access Token
const accessToken = jwt.sign(
{
sub: user.id,
name: user.name,
email: user.email,
},
SECRET_KEY,
{ expiresIn: '15m' } // 15 minutes
);

// Generate Refresh Token
const refreshToken = jwt.sign(
{ sub: user.id },
SECRET_KEY,
{ expiresIn: '7d' } // 7 days
);

res.json({
access_token: accessToken,
refresh_token: refreshToken,
token_type: 'Bearer',
expires_in: 900, // seconds
});
});

ASP.NET Core

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// JWT Authentication Configuration
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])
)
};
});

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

// Protected Endpoint
app.MapGet("/api/users/me", (HttpContext context) =>
{
var userId = context.User.FindFirst("sub")?.Value;
var userName = context.User.FindFirst("name")?.Value;

return Results.Ok(new { id = userId, name = userName });
})
.RequireAuthorization();

app.Run();

Security Considerations

Token Storage Location

❌ Store in LocalStorage (Deprecated)

// Vulnerable to XSS attacks
localStorage.setItem('accessToken', token);

Issues:

  • Accessible from JavaScript, making it highly susceptible to theft via XSS attacks.
  • Malicious scripts can read the token and send it externally.
// Manage with closures or React Context
let accessToken = null;

function setToken(token) {
accessToken = token;
}

function getToken() {
return accessToken;
}

Pros:

  • Reduces the risk of XSS attacks.
  • Token is lost on page reload (improves security).

Cons:

  • Re-login is required on every page reload.
  • Refresh token management is necessary.

Mandatory HTTPS

Since Bearer tokens are sent in plain text, HTTPS must be used.

// Enforce HTTPS in non-development environments
if (process.env.NODE_ENV === 'production' && req.protocol !== 'https') {
return res.redirect(`https://${req.hostname}${req.url}`);
}

Token Expiration

  • Access Token: Short duration (5-30 minutes)
  • Refresh Token: Long duration (days to weeks)
// Access Token: 15 minutes
const accessToken = jwt.sign(payload, SECRET, { expiresIn: '15m' });

// Refresh Token: 7 days
const refreshToken = jwt.sign({ sub: userId }, SECRET, { expiresIn: '7d' });

CORS Configuration

app.use(cors({
origin: 'https://yourapp.com',
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization'],
}));

Token Blacklisting

Implement a mechanism to invalidate tokens upon logout or token leakage.

const blacklistedTokens = new Set();

function invalidateToken(token) {
blacklistedTokens.add(token);
// Recommended to save in external storage like Redis
}

function authenticateToken(req, res, next) {
const token = req.headers['authorization']?.split(' ')[1];

if (blacklistedTokens.has(token)) {
return res.status(401).json({ error: 'Token has been revoked' });
}

// Normal token verification process
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) return res.status(403).json({ error: 'Invalid token' });
req.user = user;
next();
});
}

Pros and Cons of Bearer Authentication

Pros

  1. Stateless

    • No session management required on the server side.
    • High scalability.
  2. Standardization

    • Standard specification of OAuth 2.0.
    • Supported by many libraries and frameworks.
  3. Flexibility

    • Suitable for authentication between microservices.
    • Easy to use with mobile apps and SPAs.
  4. Cross-Domain Support

    • Less subject to CORS restrictions.

Cons

  1. Risk of XSS Attacks

    • Dangerous if stored in a location accessible from JavaScript.
  2. Token Size

    • JWT is relatively large, causing overhead in every request.
  3. Difficulty in Token Invalidation

    • Difficult to invalidate tokens immediately due to statelessness.
    • Implementation of a blacklist is required.
  4. Expiration Management

    • Refresh token mechanism is required.

Summary

  • Bearer authentication is token-based authentication using the HTTP Authorization header.
  • JWT is the most common token format.
  • To prevent XSS, manage tokens in memory, not LocalStorage.
  • HTTPS is mandatory.
  • A design with short-lived access tokens and refresh tokens is recommended.

References