Overview
The Blog Marketing Platform uses JWT (JSON Web Tokens) for authentication. This page covers token management, including refreshing expired tokens and validating token status.
All tokens are JWT format and contain user information in the payload. Access tokens expire after 24 hours, while refresh tokens have a longer lifespan.
Token Types
Access Token
Purpose : Used to authenticate API requests
Lifespan : 24 hours from issuance
Usage : Include in the Authorization header as a Bearer token:
Authorization: Bearer <accessToken>
Structure :
Refresh Token
Purpose : Used to obtain a new access token when the current one expires
Lifespan : Extended period (implementation-dependent)
Usage : Send to the refresh endpoint when access token expires
Storage : Store securely in httpOnly cookies or secure storage (never in localStorage for production)
Refresh tokens are sensitive credentials. Never expose them in logs, URLs, or client-side JavaScript. Use httpOnly cookies in production environments.
Refresh Token Endpoint
Overview
Obtain a new access token using a valid refresh token. This endpoint should be called when the access token expires or is about to expire.
Request
Body Parameters
Valid refresh token received during login or registration. Format : JWT token string
Response
New JWT access token valid for 24 hours
New refresh token (optional - may return the same or a new refresh token depending on implementation)
Updated user information (optional - may include latest user data)
Code Examples
TypeScript
JavaScript
Python
cURL
interface RefreshTokenRequest {
refreshToken : string ;
}
interface RefreshTokenResponse {
accessToken : string ;
refreshToken ?: string ;
user ?: User ;
}
async function refreshAccessToken () : Promise < string > {
const refreshToken = localStorage . getItem ( 'refresh_token' );
if ( ! refreshToken ) {
throw new Error ( 'No refresh token available' );
}
const response = await fetch ( 'https://api.example.com/api/v1/auths/refresh' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
},
body: JSON . stringify ({ refreshToken }),
});
if ( ! response . ok ) {
// Refresh token is invalid or expired, user needs to log in again
localStorage . removeItem ( 'access_token' );
localStorage . removeItem ( 'refresh_token' );
throw new Error ( 'Refresh token expired' );
}
const data = await response . json ();
// Update stored tokens
localStorage . setItem ( 'access_token' , data . accessToken );
if ( data . refreshToken ) {
localStorage . setItem ( 'refresh_token' , data . refreshToken );
}
if ( data . user ) {
localStorage . setItem ( 'user_data' , JSON . stringify ( data . user ));
}
return data . accessToken ;
}
// Automatic token refresh with retry logic
async function apiCallWithAutoRefresh ( url : string , options : RequestInit = {}) {
const accessToken = localStorage . getItem ( 'access_token' );
const headers = {
... options . headers ,
'Authorization' : `Bearer ${ accessToken } ` ,
'Content-Type' : 'application/json' ,
};
let response = await fetch ( url , { ... options , headers });
// If token expired (401), refresh and retry
if ( response . status === 401 ) {
console . log ( 'Access token expired, refreshing...' );
const newAccessToken = await refreshAccessToken ();
// Retry request with new token
headers . Authorization = `Bearer ${ newAccessToken } ` ;
response = await fetch ( url , { ... options , headers });
}
return response ;
}
// Usage
const response = await apiCallWithAutoRefresh ( 'https://api.example.com/api/v1/posts' );
const posts = await response . json ();
Error Responses
Unauthorized - Invalid or expired refresh token{
"statusCode" : 401 ,
"message" : "Invalid refresh token"
}
Action : User must log in again
Bad Request - Missing refresh token{
"statusCode" : 400 ,
"message" : "Refresh token is required"
}
Token Validation
Client-Side Validation
You can decode and validate JWT tokens on the client side to check expiration without making an API call.
interface TokenPayload {
id : number ;
email : string ;
role : string ;
exp : number ; // Unix timestamp in seconds (for real JWT) or milliseconds (for mock)
}
function isTokenExpired ( token : string ) : boolean {
if ( ! token ) return true ;
try {
const isMockToken = ! token . includes ( '.' );
let payload : TokenPayload ;
if ( isMockToken ) {
// Mock token (base64 encoded JSON)
payload = JSON . parse ( atob ( token ));
return payload . exp < Date . now ();
} else {
// Real JWT token (format: header.payload.signature)
const parts = token . split ( '.' );
if ( parts . length !== 3 ) return true ;
// Decode payload (base64url)
const base64 = parts [ 1 ]. replace ( /-/ g , '+' ). replace ( /_/ g , '/' );
payload = JSON . parse ( atob ( base64 ));
// JWT exp is in seconds, convert to milliseconds
return payload . exp * 1000 < Date . now ();
}
} catch ( error ) {
console . error ( 'Error validating token:' , error );
return true ;
}
}
function getTokenPayload ( token : string ) : TokenPayload | null {
if ( ! token ) return null ;
try {
const isMockToken = ! token . includes ( '.' );
if ( isMockToken ) {
return JSON . parse ( atob ( token ));
} else {
const parts = token . split ( '.' );
if ( parts . length !== 3 ) return null ;
const base64 = parts [ 1 ]. replace ( /-/ g , '+' ). replace ( /_/ g , '/' );
return JSON . parse ( atob ( base64 ));
}
} catch ( error ) {
console . error ( 'Error decoding token:' , error );
return null ;
}
}
function shouldRefreshToken ( token : string ) : boolean {
const payload = getTokenPayload ( token );
if ( ! payload ) return true ;
const isMockToken = ! token . includes ( '.' );
const expirationTime = isMockToken ? payload . exp : payload . exp * 1000 ;
const timeUntilExpiry = expirationTime - Date . now ();
// Refresh if less than 5 minutes until expiry
return timeUntilExpiry < 5 * 60 * 1000 ;
}
// Usage
const token = localStorage . getItem ( 'access_token' );
if ( isTokenExpired ( token )) {
console . log ( 'Token expired, need to refresh' );
await refreshAccessToken ();
} else if ( shouldRefreshToken ( token )) {
console . log ( 'Token expiring soon, refreshing proactively' );
await refreshAccessToken ();
}
Client-side token validation only checks the expiration time and structure. It does NOT verify the token signature. Server-side validation is still required for security.
Token Refresh Strategies
Strategy 1: Reactive Refresh (On 401)
Refresh the token only when an API call returns 401 Unauthorized.
Pros : Simple implementation, no unnecessary refresh calls
Cons : User may experience a brief delay on the first expired request
// Implemented in "Automatic token refresh with retry logic" example above
Strategy 2: Proactive Refresh (Before Expiry)
Refresh the token before it expires (e.g., 5 minutes before expiration).
Pros : Seamless user experience, no API call failures
Cons : Requires periodic checks, may refresh unnecessarily
function setupTokenRefreshTimer () {
setInterval ( async () => {
const token = localStorage . getItem ( 'access_token' );
if ( shouldRefreshToken ( token )) {
try {
await refreshAccessToken ();
console . log ( 'Token refreshed proactively' );
} catch ( error ) {
console . error ( 'Failed to refresh token:' , error );
}
}
}, 60000 ); // Check every minute
}
// Start timer on app initialization
setupTokenRefreshTimer ();
Strategy 3: Hybrid Approach (Recommended)
Combine both strategies: proactive refresh with 401 fallback.
// Use proactive refresh timer
setupTokenRefreshTimer ();
// AND use automatic refresh on 401
apiCallWithAutoRefresh ( url , options );
Best Practices
Follow these best practices for secure token management:
Storage
Development : localStorage is acceptable for development
Production : Use httpOnly cookies or secure storage mechanisms
Never : Store tokens in session storage, URL parameters, or local variables that persist
Security
Always use HTTPS in production to prevent token interception
Implement token rotation : Issue new refresh tokens periodically
Set appropriate token lifespans : Short for access tokens (minutes to hours), longer for refresh tokens (days to weeks)
Implement logout : Clear all tokens and invalidate on the server
Monitor for suspicious activity : Track token usage patterns
Error Handling
try {
await refreshAccessToken ();
} catch ( error ) {
// Refresh failed - clear tokens and redirect to login
localStorage . removeItem ( 'access_token' );
localStorage . removeItem ( 'refresh_token' );
localStorage . removeItem ( 'user_data' );
window . location . href = '/login' ;
}
Concurrent Requests
When multiple API calls are made simultaneously and the token expires, prevent multiple refresh requests:
let refreshPromise : Promise < string > | null = null ;
async function getValidAccessToken () : Promise < string > {
const token = localStorage . getItem ( 'access_token' );
if ( ! isTokenExpired ( token )) {
return token ! ;
}
// If already refreshing, wait for that promise
if ( refreshPromise ) {
return refreshPromise ;
}
// Start new refresh
refreshPromise = refreshAccessToken ()
. finally (() => {
refreshPromise = null ;
});
return refreshPromise ;
}
Token Lifecycle Diagram
Login - Obtain initial tokens
Register - Create account and obtain tokens