Skip to main content

Refresh Token

Overview​

Refresh JWT access tokens without requiring the user to re-authenticate. This extends user sessions securely while maintaining security.

Purpose​

JWT access tokens have short expiration times (typically 15-60 minutes) for security. Refresh tokens allow:

  • Seamless token renewal without user interaction
  • Longer session duration while maintaining security
  • Ability to revoke access by invalidating refresh tokens

Endpoint​

POST /api/Auth/RefreshToken

Request Parameters​

ParameterTypeRequiredDescription
refreshTokenstringYesThe refresh token received during login

Request​

{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response​

Successful Refresh​

{
"status": "success",
"message": "Token refreshed successfully",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 3600
}
}

Invalid Refresh Token​

{
"status": "error",
"message": "Invalid or expired refresh token",
"errorCode": "AUTH_010"
}

Token Lifecycle​

Implementation Examples​

React with Axios Interceptor​

import axios from 'axios';

// Create axios instance
const api = axios.create({
baseURL: 'https://api.banklingo.com'
});

// Request interceptor: Add token to headers
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('jwt_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);

// Response interceptor: Handle token refresh
api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;

// If 401 and not already retried
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;

try {
const refreshToken = localStorage.getItem('refresh_token');
const response = await axios.post('/api/Auth/RefreshToken', {
refreshToken
});

const { token, refreshToken: newRefreshToken } = response.data.data;

// Update stored tokens
localStorage.setItem('jwt_token', token);
localStorage.setItem('refresh_token', newRefreshToken);

// Retry original request with new token
originalRequest.headers.Authorization = `Bearer ${token}`;
return api(originalRequest);
} catch (refreshError) {
// Refresh failed, redirect to login
localStorage.clear();
window.location.href = '/login';
return Promise.reject(refreshError);
}
}

return Promise.reject(error);
}
);

export default api;

C# HttpClient​

Code Removed

Implementation details removed for security.

Contact support for implementation guidance.

JavaScript Fetch API​

class ApiClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}

async fetch(url, options = {}) {
// Add token to headers
const token = localStorage.getItem('jwt_token');
const headers = {
'Content-Type': 'application/json',
...options.headers
};

if (token) {
headers.Authorization = `Bearer ${token}`;
}

let response = await fetch(`${this.baseUrl}${url}`, {
...options,
headers
});

// If 401, try to refresh
if (response.status === 401 && !options._retry) {
const newToken = await this.refreshToken();

if (newToken) {
// Retry with new token
headers.Authorization = `Bearer ${newToken}`;
response = await fetch(`${this.baseUrl}${url}`, {
...options,
headers,
_retry: true
});
} else {
// Refresh failed, redirect to login
window.location.href = '/login';
throw new Error('Session expired');
}
}

return response;
}

async refreshToken() {
const refreshToken = localStorage.getItem('refresh_token');

if (!refreshToken) {
return null;
}

try {
const response = await fetch(`${this.baseUrl}/api/Auth/RefreshToken`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken })
});

if (response.ok) {
const result = await response.json();
const { token, refreshToken: newRefreshToken } = result.data;

localStorage.setItem('jwt_token', token);
localStorage.setItem('refresh_token', newRefreshToken);

return token;
}
} catch (error) {
console.error('Token refresh failed:', error);
}

// Refresh failed, clear tokens
localStorage.removeItem('jwt_token');
localStorage.removeItem('refresh_token');
return null;
}
}

// Usage
const api = new ApiClient('https://api.banklingo.com');
const response = await api.fetch('/api/Accounts/List');

Token Storage​

// Server sets HttpOnly cookie (most secure)
// JavaScript cannot access the token
// Automatically sent with requests
// Protected from XSS attacks

Alternative: Local Storage​

// Store tokens in localStorage
localStorage.setItem('jwt_token', token);
localStorage.setItem('refresh_token', refreshToken);

// Retrieve tokens
const token = localStorage.getItem('jwt_token');
const refreshToken = localStorage.getItem('refresh_token');

// Clear on logout
localStorage.removeItem('jwt_token');
localStorage.removeItem('refresh_token');

Security Comparison​

StorageXSS ProtectionCSRF ProtectionBest For
HttpOnly CookieÒœ… YesÒő ï¸ Requires CSRF tokenWeb apps
LocalStorageҝŒ NoÒœ… YesSPAs with strict CSP
SessionStorageҝŒ NoÒœ… YesSingle-tab apps
Memory onlyÒœ… YesÒœ… YesHigh security apps

Token Expiration Times​

Token TypeTypical DurationPurpose
Access Token15-60 minutesAPI authentication
Refresh Token7-30 daysToken renewal
Setup Token5-10 minutesAuthenticator setup

Error Responses​

StatusError CodeDescription
401AUTH_010Invalid refresh token
401AUTH_011Refresh token expired
403AUTH_012Refresh token revoked
400AUTH_013Missing refresh token

Best Practices​

Security​

  1. Short access token expiration (15-60 minutes)
  2. Store refresh tokens securely (HttpOnly cookies preferred)
  3. Rotate refresh tokens on each refresh
  4. Revoke refresh tokens on logout
  5. Monitor token usage for anomalies

User Experience​

  1. Silent refresh: Refresh automatically before expiration
  2. Graceful degradation: Handle refresh failures elegantly
  3. Clear communication: Show login prompt when refresh fails
  4. Preserve state: Retain user's work during refresh

Implementation​

  1. Use interceptors: Handle refresh automatically
  2. Queue requests: Prevent multiple simultaneous refresh attempts
  3. Retry failed requests: After successful refresh
  4. Clear tokens on logout: Prevent token reuse

See Also​