No articles found
Try different keywords or browse our categories
Fix: Token not stored in localStorage Error
Learn how to fix the 'Token not stored in localStorage' error in web applications. This comprehensive guide covers localStorage access, security policies, and proper token storage solutions.
The ‘Token not stored in localStorage’ error is a common web development issue that occurs when an application attempts to store authentication tokens in the browser’s localStorage but fails due to various factors. This error typically happens during login processes, token refresh operations, or when implementing authentication flows. The error can manifest as SecurityError, QuotaExceededError, or silent failures where tokens appear to be set but aren’t actually stored.
This comprehensive guide explains what causes this error, why it happens, and provides multiple solutions to fix it in your web projects with clean code examples and directory structure.
What is the Token not stored in localStorage Error?
The “Token not stored in localStorage” error occurs when:
- Attempting to store tokens in localStorage fails silently
- Browser security policies block localStorage access
- Storage quota is exceeded
- Application runs in a restricted environment
- Third-party cookies or storage are disabled
- Incognito/private browsing mode is active
Common Error Messages:
Uncaught DOMException: Failed to execute 'setItem' on 'Storage': Setting the value of 'token' exceeded the quotaSecurityError: The operation is insecurelocalStorage is not definedCannot set property of undefinedStorage access is restricted in this context
Understanding the Problem
Modern web applications rely heavily on localStorage to store authentication tokens, user preferences, and session data. When token storage fails, users may experience authentication issues, unexpected logouts, or inability to access protected resources. The problem often stems from browser security restrictions, storage limitations, or execution context issues.
Typical Web Application Project Structure:
my-auth-app/
├── package.json
├── index.html
├── src/
│ ├── js/
│ │ ├── auth.js
│ │ ├── tokenManager.js
│ │ └── utils.js
│ ├── css/
│ │ └── styles.css
│ └── pages/
│ ├── login.html
│ └── dashboard.html
├── public/
└── dist/
Solution 1: Check for localStorage Availability
The most common cause of this error is localStorage not being available in the current context. Always check for availability before attempting to store tokens.
❌ Without Availability Check:
// auth.js - ❌ No localStorage availability check
class AuthManager {
storeToken(token) {
// ❌ This will fail if localStorage is not available
localStorage.setItem('authToken', token);
}
getToken() {
// ❌ This will fail if localStorage is not available
return localStorage.getItem('authToken');
}
}
// ❌ This will cause errors in restricted environments
const auth = new AuthManager();
auth.storeToken('your-jwt-token');
✅ With Availability Check:
utils/storage.js:
// ✅ Utility to check localStorage availability
class StorageUtil {
static isLocalStorageAvailable() {
try {
// ✅ Test localStorage availability
const testKey = '__storage_test__';
localStorage.setItem(testKey, testKey);
localStorage.removeItem(testKey);
return true;
} catch (error) {
// ✅ localStorage is not available
console.warn('localStorage is not available:', error);
return false;
}
}
static setItem(key, value) {
if (!this.isLocalStorageAvailable()) {
console.error('localStorage not available, cannot store:', key);
return false;
}
try {
localStorage.setItem(key, value);
return true;
} catch (error) {
console.error('Failed to store item in localStorage:', error);
return false;
}
}
static getItem(key) {
if (!this.isLocalStorageAvailable()) {
console.error('localStorage not available, cannot retrieve:', key);
return null;
}
try {
return localStorage.getItem(key);
} catch (error) {
console.error('Failed to retrieve item from localStorage:', error);
return null;
}
}
static removeItem(key) {
if (!this.isLocalStorageAvailable()) {
console.error('localStorage not available, cannot remove:', key);
return false;
}
try {
localStorage.removeItem(key);
return true;
} catch (error) {
console.error('Failed to remove item from localStorage:', error);
return false;
}
}
static clear() {
if (!this.isLocalStorageAvailable()) {
console.error('localStorage not available, cannot clear');
return false;
}
try {
localStorage.clear();
return true;
} catch (error) {
console.error('Failed to clear localStorage:', error);
return false;
}
}
}
module.exports = StorageUtil;
tokenManager.js:
// ✅ Token manager with localStorage availability check
const StorageUtil = require('./utils/storage');
class TokenManager {
constructor() {
this.storageKey = 'authToken';
}
storeToken(token) {
// ✅ Check availability before storing
if (!StorageUtil.isLocalStorageAvailable()) {
console.error('Cannot store token: localStorage not available');
return false;
}
// ✅ Store token with error handling
return StorageUtil.setItem(this.storageKey, token);
}
getToken() {
// ✅ Check availability before retrieving
if (!StorageUtil.isLocalStorageAvailable()) {
console.error('Cannot retrieve token: localStorage not available');
return null;
}
return StorageUtil.getItem(this.storageKey);
}
removeToken() {
// ✅ Check availability before removing
if (!StorageUtil.isLocalStorageAvailable()) {
console.error('Cannot remove token: localStorage not available');
return false;
}
return StorageUtil.removeItem(this.storageKey);
}
isTokenValid() {
const token = this.getToken();
if (!token) {
return false;
}
// ✅ Simple token validation (check expiration)
try {
const payload = JSON.parse(atob(token.split('.')[1]));
const currentTime = Math.floor(Date.now() / 1000);
return payload.exp > currentTime;
} catch (error) {
console.error('Token validation failed:', error);
return false;
}
}
}
module.exports = TokenManager;
Solution 2: Handle Storage Quota Exceeded Errors
localStorage has limited storage capacity (typically 5-10MB depending on the browser). Handle quota exceeded errors gracefully.
utils/storage.js (enhanced):
// ✅ Enhanced storage utility with quota handling
class StorageUtil {
static isLocalStorageAvailable() {
try {
const testKey = '__storage_test__';
localStorage.setItem(testKey, testKey);
localStorage.removeItem(testKey);
return true;
} catch (error) {
console.warn('localStorage is not available:', error);
return false;
}
}
static setItem(key, value) {
if (!this.isLocalStorageAvailable()) {
console.error('localStorage not available, cannot store:', key);
return false;
}
try {
// ✅ Check if we're approaching storage limits
const estimatedSize = this.estimateStorageSize(key, value);
if (estimatedSize > this.getMaxStorageSize()) {
console.warn('Estimated storage size exceeds limits:', estimatedSize);
// ✅ Try to free up space
this.cleanupOldEntries();
}
localStorage.setItem(key, value);
return true;
} catch (error) {
if (error.name === 'QuotaExceededError') {
console.error('Storage quota exceeded, attempting cleanup...');
// ✅ Try to free up space and retry
this.cleanupOldEntries();
try {
localStorage.setItem(key, value);
return true;
} catch (retryError) {
console.error('Failed to store even after cleanup:', retryError);
return false;
}
} else {
console.error('Failed to store item in localStorage:', error);
return false;
}
}
}
static estimateStorageSize(key, value) {
// ✅ Estimate storage size in bytes
return new Blob([key + value]).size;
}
static getMaxStorageSize() {
// ✅ Conservative estimate of max storage size (5MB)
return 5 * 1024 * 1024;
}
static cleanupOldEntries() {
// ✅ Remove old entries to free up space
const now = Date.now();
const keysToRemove = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
// ✅ Look for keys with timestamp metadata
if (key && key.endsWith('_timestamp')) {
const timestamp = localStorage.getItem(key);
const storedKey = key.replace('_timestamp', '');
// ✅ Remove entries older than 30 days
if (now - parseInt(timestamp) > 30 * 24 * 60 * 60 * 1000) {
keysToRemove.push(storedKey);
keysToRemove.push(key);
}
}
}
// ✅ Remove expired entries
keysToRemove.forEach(key => {
try {
localStorage.removeItem(key);
} catch (error) {
console.error('Failed to remove key:', key, error);
}
});
}
static getItem(key) {
if (!this.isLocalStorageAvailable()) {
console.error('localStorage not available, cannot retrieve:', key);
return null;
}
try {
return localStorage.getItem(key);
} catch (error) {
console.error('Failed to retrieve item from localStorage:', error);
return null;
}
}
static removeItem(key) {
if (!this.isLocalStorageAvailable()) {
console.error('localStorage not available, cannot remove:', key);
return false;
}
try {
localStorage.removeItem(key);
return true;
} catch (error) {
console.error('Failed to remove item from localStorage:', error);
return false;
}
}
static clear() {
if (!this.isLocalStorageAvailable()) {
console.error('localStorage not available, cannot clear');
return false;
}
try {
localStorage.clear();
return true;
} catch (error) {
console.error('Failed to clear localStorage:', error);
return false;
}
}
}
module.exports = StorageUtil;
Solution 3: Use Alternative Storage Methods
When localStorage is unavailable, implement fallback storage methods.
utils/tokenStorage.js:
// ✅ Token storage with multiple fallback options
class TokenStorage {
constructor() {
this.storageMethods = [
this.localStorage,
this.sessionStorage,
this.cookieStorage,
this.memoryStorage
];
}
// ✅ localStorage method
localStorage = {
set: (key, value) => {
if (this.isStorageAvailable('localStorage')) {
localStorage.setItem(key, value);
return true;
}
return false;
},
get: (key) => {
if (this.isStorageAvailable('localStorage')) {
return localStorage.getItem(key);
}
return null;
},
remove: (key) => {
if (this.isStorageAvailable('localStorage')) {
localStorage.removeItem(key);
return true;
}
return false;
}
};
// ✅ sessionStorage method
sessionStorage = {
set: (key, value) => {
if (this.isStorageAvailable('sessionStorage')) {
sessionStorage.setItem(key, value);
return true;
}
return false;
},
get: (key) => {
if (this.isStorageAvailable('sessionStorage')) {
return sessionStorage.getItem(key);
}
return null;
},
remove: (key) => {
if (this.isStorageAvailable('sessionStorage')) {
sessionStorage.removeItem(key);
return true;
}
return false;
}
};
// ✅ Cookie storage method
cookieStorage = {
set: (key, value, days = 7) => {
try {
const expires = new Date();
expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
document.cookie = `${key}=${value};expires=${expires.toUTCString()};path=/`;
return true;
} catch (error) {
console.error('Cookie storage failed:', error);
return false;
}
},
get: (key) => {
try {
const name = key + "=";
const decodedCookie = decodeURIComponent(document.cookie);
const ca = decodedCookie.split(';');
for(let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
}
}
return null;
} catch (error) {
console.error('Cookie retrieval failed:', error);
return null;
}
},
remove: (key) => {
try {
document.cookie = `${key}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;`;
return true;
} catch (error) {
console.error('Cookie removal failed:', error);
return false;
}
}
};
// ✅ Memory storage method (temporary)
memoryStorage = (() => {
let storage = {};
return {
set: (key, value) => {
storage[key] = value;
return true;
},
get: (key) => {
return storage[key] || null;
},
remove: (key) => {
delete storage[key];
return true;
}
};
})();
isStorageAvailable(type) {
try {
const storage = window[type];
const testKey = '__storage_test__';
storage.setItem(testKey, testKey);
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
}
// ✅ Find the first available storage method
getAvailableStorage() {
for (const method of this.storageMethods) {
// ✅ Test if this method works
const testKey = '__test_storage__';
if (method.set(testKey, 'test') && method.get(testKey) === 'test') {
method.remove(testKey);
return method;
}
}
return null;
}
// ✅ Store token using available method
storeToken(key, token) {
const storage = this.getAvailableStorage();
if (storage) {
return storage.set(key, token);
}
console.error('No storage method available');
return false;
}
// ✅ Retrieve token using available method
getToken(key) {
const storage = this.getAvailableStorage();
if (storage) {
return storage.get(key);
}
console.error('No storage method available');
return null;
}
// ✅ Remove token using available method
removeToken(key) {
const storage = this.getAvailableStorage();
if (storage) {
return storage.remove(key);
}
console.error('No storage method available');
return false;
}
}
module.exports = TokenStorage;
Solution 4: Handle Different Execution Contexts
Handle cases where localStorage might not be available due to execution context (workers, iframes, etc.).
auth.js:
// ✅ Authentication manager with context awareness
const TokenStorage = require('./utils/tokenStorage');
class AuthManager {
constructor() {
this.tokenStorage = new TokenStorage();
this.storageKey = 'authToken';
}
// ✅ Check if running in a secure context
isSecureContext() {
return window.isSecureContext || location.protocol === 'https:';
}
// ✅ Check if running in a browser environment
isBrowserEnvironment() {
return typeof window !== 'undefined' && typeof localStorage !== 'undefined';
}
// ✅ Store token with context awareness
storeToken(token) {
if (!this.isBrowserEnvironment()) {
console.warn('Not in browser environment, token storage unavailable');
return false;
}
// ✅ Check for iframe context
if (this.isInIframe() && !this.isSameOriginIframe()) {
console.warn('Running in cross-origin iframe, storage may be restricted');
}
return this.tokenStorage.storeToken(this.storageKey, token);
}
// ✅ Retrieve token with context awareness
getToken() {
if (!this.isBrowserEnvironment()) {
console.warn('Not in browser environment, token retrieval unavailable');
return null;
}
return this.tokenStorage.getToken(this.storageKey);
}
// ✅ Check if running in iframe
isInIframe() {
try {
return window.self !== window.top;
} catch (e) {
return true; // If we can't access window.top, we're probably in an iframe
}
}
// ✅ Check if iframe is same origin
isSameOriginIframe() {
try {
return window.location.hostname === window.parent.location.hostname;
} catch (e) {
return false; // Cross-origin iframe
}
}
// ✅ Login with proper token storage
async login(credentials) {
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(credentials)
});
if (!response.ok) {
throw new Error('Login failed');
}
const data = await response.json();
// ✅ Store token with error handling
const stored = this.storeToken(data.token);
if (!stored) {
console.error('Failed to store token, login may not persist');
}
return data;
} catch (error) {
console.error('Login error:', error);
throw error;
}
}
// ✅ Logout with proper token removal
logout() {
const removed = this.tokenStorage.removeToken(this.storageKey);
if (!removed) {
console.error('Failed to remove token');
}
// ✅ Redirect to login page
window.location.href = '/login';
}
// ✅ Check if user is authenticated
isAuthenticated() {
const token = this.getToken();
if (!token) {
return false;
}
// ✅ Validate token (simple validation)
try {
const payload = JSON.parse(atob(token.split('.')[1]));
const currentTime = Math.floor(Date.now() / 1000);
return payload.exp > currentTime;
} catch (error) {
console.error('Token validation failed:', error);
return false;
}
}
}
module.exports = AuthManager;
Solution 5: Implement Token Storage with Retry Logic
Add retry logic to handle temporary storage failures.
utils/retryableStorage.js:
// ✅ Token storage with retry logic
class RetryableTokenStorage {
constructor() {
this.tokenStorage = new TokenStorage();
this.maxRetries = 3;
this.retryDelay = 100; // ms
}
async storeTokenWithRetry(key, token) {
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
const success = this.tokenStorage.storeToken(key, token);
if (success) {
return true;
}
} catch (error) {
console.warn(`Token storage attempt ${attempt} failed:`, error);
}
if (attempt < this.maxRetries) {
// ✅ Wait before retrying
await this.delay(this.retryDelay * attempt);
}
}
console.error(`Failed to store token after ${this.maxRetries} attempts`);
return false;
}
async delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// ✅ Store token with retry and fallback
async storeToken(key, token) {
// ✅ First, try with retry logic
const success = await this.storeTokenWithRetry(key, token);
if (!success) {
// ✅ If all retries fail, try alternative approaches
console.warn('Token storage failed, trying alternative methods...');
// ✅ Try to store in memory as last resort
this.inMemoryTokens = this.inMemoryTokens || {};
this.inMemoryTokens[key] = token;
// ✅ Warn user about temporary storage
this.showTemporaryStorageWarning();
}
return success;
}
// ✅ Show warning about temporary storage
showTemporaryStorageWarning() {
if (typeof window !== 'undefined' && window.alert) {
alert('⚠️ Warning: Token storage is not persistent. You may be logged out when closing the browser.');
}
}
// ✅ Get token with fallback to memory storage
getToken(key) {
// ✅ Try primary storage first
let token = this.tokenStorage.getToken(key);
if (!token && this.inMemoryTokens && this.inMemoryTokens[key]) {
// ✅ Fall back to memory storage
token = this.inMemoryTokens[key];
console.warn('Using temporary token from memory storage');
}
return token;
}
// ✅ Remove token
removeToken(key) {
const success = this.tokenStorage.removeToken(key);
// ✅ Also remove from memory storage if present
if (this.inMemoryTokens && this.inMemoryTokens[key]) {
delete this.inMemoryTokens[key];
}
return success;
}
}
module.exports = RetryableTokenStorage;
Solution 6: Use Modern Storage APIs
Consider using newer storage APIs that may be more reliable.
utils/modernStorage.js:
// ✅ Modern storage using newer APIs where available
class ModernTokenStorage {
constructor() {
this.fallbackStorage = new TokenStorage();
}
// ✅ Use IndexedDB for larger storage needs
async storeTokenIndexedDB(key, token) {
if (!window.indexedDB) {
return false;
}
try {
const dbPromise = this.openDatabase();
const db = await dbPromise;
const transaction = db.transaction(['tokens'], 'readwrite');
const store = transaction.objectStore('tokens');
store.put({ key, value: token, timestamp: Date.now() });
return new Promise((resolve, reject) => {
transaction.oncomplete = () => resolve(true);
transaction.onerror = () => reject(transaction.error);
});
} catch (error) {
console.error('IndexedDB storage failed:', error);
return false;
}
}
async openDatabase() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('TokenDB', 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('tokens')) {
const store = db.createObjectStore('tokens', { keyPath: 'key' });
store.createIndex('timestamp', 'timestamp', { unique: false });
}
};
});
}
async getTokenIndexedDB(key) {
if (!window.indexedDB) {
return null;
}
try {
const dbPromise = this.openDatabase();
const db = await dbPromise;
const transaction = db.transaction(['tokens'], 'readonly');
const store = transaction.objectStore('tokens');
const request = store.get(key);
return new Promise((resolve, reject) => {
request.onsuccess = () => {
const result = request.result;
resolve(result ? result.value : null);
};
request.onerror = () => reject(request.error);
});
} catch (error) {
console.error('IndexedDB retrieval failed:', error);
return null;
}
}
// ✅ Main storage method with multiple options
async storeToken(key, token) {
// ✅ Try IndexedDB first
if (await this.storeTokenIndexedDB(key, token)) {
return true;
}
// ✅ Fall back to traditional methods
return this.fallbackStorage.storeToken(key, token);
}
async getToken(key) {
// ✅ Try IndexedDB first
const indexedDBToken = await this.getTokenIndexedDB(key);
if (indexedDBToken) {
return indexedDBToken;
}
// ✅ Fall back to traditional methods
return this.fallbackStorage.getToken(key);
}
async removeToken(key) {
// ✅ Remove from both IndexedDB and traditional storage
let success = true;
if (window.indexedDB) {
try {
const dbPromise = this.openDatabase();
const db = await dbPromise;
const transaction = db.transaction(['tokens'], 'readwrite');
const store = transaction.objectStore('tokens');
store.delete(key);
await new Promise((resolve, reject) => {
transaction.oncomplete = () => resolve();
transaction.onerror = () => reject(transaction.error);
});
} catch (error) {
console.error('IndexedDB removal failed:', error);
success = false;
}
}
// ✅ Also remove from traditional storage
const traditionalSuccess = this.fallbackStorage.removeToken(key);
return success && traditionalSuccess;
}
}
module.exports = ModernTokenStorage;
Working Code Examples
Complete Authentication System with Token Storage:
// src/js/auth.js
const ModernTokenStorage = require('./utils/modernStorage');
class AuthSystem {
constructor() {
this.tokenStorage = new ModernTokenStorage();
this.storageKey = 'authToken';
this.refreshThreshold = 5 * 60 * 1000; // 5 minutes before expiry
}
// ✅ Initialize auth system
async init() {
// ✅ Check for existing token
const token = this.getToken();
if (token && this.isTokenValid(token)) {
// ✅ Token is valid, set up auto-refresh
this.scheduleTokenRefresh(token);
}
}
// ✅ Store token with error handling
async storeToken(token) {
const success = await this.tokenStorage.storeToken(this.storageKey, token);
if (success) {
console.log('Token stored successfully');
// ✅ Schedule refresh if token is valid
if (this.isTokenValid(token)) {
this.scheduleTokenRefresh(token);
}
} else {
console.error('Failed to store token');
// ✅ Show user notification
this.showStorageErrorNotification();
}
return success;
}
// ✅ Get token
getToken() {
return this.tokenStorage.getToken(this.storageKey);
}
// ✅ Remove token
removeToken() {
return this.tokenStorage.removeToken(this.storageKey);
}
// ✅ Validate token
isTokenValid(token) {
if (!token) return false;
try {
const payload = JSON.parse(atob(token.split('.')[1]));
const currentTime = Math.floor(Date.now() / 1000);
return payload.exp > currentTime;
} catch (error) {
console.error('Token validation failed:', error);
return false;
}
}
// ✅ Schedule token refresh
scheduleTokenRefresh(token) {
try {
const payload = JSON.parse(atob(token.split('.')[1]));
const expiryTime = payload.exp * 1000;
const currentTime = Date.now();
const timeUntilRefresh = Math.max(0, expiryTime - this.refreshThreshold - currentTime);
// ✅ Clear any existing timeout
if (this.refreshTimeout) {
clearTimeout(this.refreshTimeout);
}
// ✅ Schedule refresh
this.refreshTimeout = setTimeout(async () => {
await this.refreshToken();
}, timeUntilRefresh);
console.log(`Token refresh scheduled in ${Math.round(timeUntilRefresh / 1000)} seconds`);
} catch (error) {
console.error('Failed to schedule token refresh:', error);
}
}
// ✅ Refresh token
async refreshToken() {
const refreshToken = this.getRefreshToken();
if (!refreshToken) {
console.error('No refresh token available');
this.logout();
return;
}
try {
const response = await fetch('/api/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${refreshToken}`
}
});
if (!response.ok) {
throw new Error('Token refresh failed');
}
const data = await response.json();
await this.storeToken(data.token);
console.log('Token refreshed successfully');
} catch (error) {
console.error('Token refresh failed:', error);
this.logout();
}
}
// ✅ Get refresh token
getRefreshToken() {
return this.tokenStorage.getToken('refreshToken');
}
// ✅ Login
async login(credentials) {
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(credentials)
});
if (!response.ok) {
throw new Error('Login failed');
}
const data = await response.json();
// ✅ Store tokens
const tokenStored = await this.storeToken(data.token);
const refreshTokenStored = await this.tokenStorage.storeToken('refreshToken', data.refreshToken);
if (!tokenStored || !refreshTokenStored) {
console.warn('Some tokens were not stored properly');
}
return data;
} catch (error) {
console.error('Login error:', error);
throw error;
}
}
// ✅ Logout
logout() {
this.removeToken();
this.tokenStorage.removeToken('refreshToken');
// ✅ Clear refresh timeout
if (this.refreshTimeout) {
clearTimeout(this.refreshTimeout);
}
// ✅ Redirect to login
window.location.href = '/login';
}
// ✅ Show storage error notification
showStorageErrorNotification() {
if (typeof window !== 'undefined') {
const notification = document.createElement('div');
notification.className = 'storage-error-notification';
notification.innerHTML = `
<div style="position: fixed; top: 20px; right: 20px; background: #ff4444; color: white; padding: 10px; border-radius: 4px; z-index: 1000;">
⚠️ Warning: Token storage is not working. You may be logged out when closing the browser.
</div>
`;
document.body.appendChild(notification);
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 5000);
}
}
}
// ✅ Initialize auth system
document.addEventListener('DOMContentLoaded', async () => {
const auth = new AuthSystem();
await auth.init();
// ✅ Make auth available globally
window.auth = auth;
});
HTML Login Page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Token Storage Demo</title>
</head>
<body>
<div class="login-container">
<h1>Login</h1>
<form id="loginForm">
<div>
<label for="username">Username:</label>
<input type="text" id="username" required>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" required>
</div>
<button type="submit">Login</button>
</form>
<div id="statusMessage"></div>
</div>
<script src="auth.js"></script>
<script>
document.getElementById('loginForm').addEventListener('submit', async (e) => {
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
try {
await window.auth.login({ username, password });
document.getElementById('statusMessage').innerHTML = '<p>Login successful!</p>';
window.location.href = '/dashboard';
} catch (error) {
document.getElementById('statusMessage').innerHTML = `<p style="color: red;">Login failed: ${error.message}</p>`;
}
});
</script>
</body>
</html>
Best Practices for Token Storage
1. Always Check Availability
// ✅ Always check if storage is available
if (StorageUtil.isLocalStorageAvailable()) {
localStorage.setItem('token', token);
}
2. Handle Quota Exceeded Errors
// ✅ Handle quota exceeded errors gracefully
try {
localStorage.setItem('token', token);
} catch (error) {
if (error.name === 'QuotaExceededError') {
// ✅ Clean up and retry
cleanupOldEntries();
}
}
3. Use Secure Storage Options
// ✅ Consider HttpOnly cookies for sensitive tokens
// Store non-sensitive info in localStorage
4. Implement Fallback Storage
// ✅ Implement fallback storage methods
const storage = getAvailableStorage();
if (storage) {
storage.setItem('token', token);
}
5. Validate Tokens Before Storage
// ✅ Validate tokens before storing
if (isTokenValid(token)) {
localStorage.setItem('token', token);
}
Debugging Steps
Step 1: Check Browser Console
# Open browser dev tools
# Check Console tab for storage errors
# Look for SecurityError or QuotaExceededError messages
Step 2: Inspect Storage
# Open browser dev tools
# Go to Application/Storage tab
# Check localStorage contents
# Verify if token is actually stored
Step 3: Test Storage Availability
// Test localStorage availability
try {
const test = '__test__';
localStorage.setItem(test, test);
localStorage.removeItem(test);
console.log('localStorage is available');
} catch (e) {
console.log('localStorage is not available:', e);
}
Step 4: Check Browser Settings
# Verify browser settings
# Check if third-party cookies are blocked
# Verify incognito/private mode is not active
# Check if storage is disabled
Common Mistakes to Avoid
1. Not Checking Storage Availability
// ❌ Don't assume localStorage is always available
localStorage.setItem('token', token); // ❌ May fail silently
2. Ignoring Quota Limits
// ❌ Don't ignore storage quota
const largeToken = generateLargeToken();
localStorage.setItem('token', largeToken); // ❌ May exceed quota
3. Not Handling Context Restrictions
// ❌ Don't ignore execution context
// This may fail in iframes or workers
localStorage.setItem('token', token);
4. Storing Sensitive Data in localStorage
// ❌ Don't store highly sensitive tokens in localStorage
// Consider HttpOnly cookies for better security
localStorage.setItem('sensitiveToken', sensitiveData);
Performance Considerations
1. Minimize Storage Operations
// ✅ Batch storage operations when possible
// Instead of multiple setItem calls, store objects
localStorage.setItem('userData', JSON.stringify(userData));
2. Monitor Storage Usage
// ✅ Monitor storage usage
function getStorageUsage() {
let total = 0;
for (let key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
total += localStorage[key].length + key.length;
}
}
return total;
}
3. Clean Up Old Entries
// ✅ Regularly clean up old entries
function cleanupExpiredTokens() {
// Remove expired tokens
}
Security Considerations
1. XSS Protection
// ✅ Protect against XSS attacks
// Don't store tokens that could be accessed by malicious scripts
2. HTTPS Requirement
// ✅ Use HTTPS in production
// Tokens should only be stored in secure contexts
3. Token Expiration
// ✅ Implement proper token expiration
// Don't store expired tokens indefinitely
Testing Token Storage
1. Unit Test Storage Functions
// Using Jest or similar testing framework
const StorageUtil = require('../utils/storage');
describe('Storage Utility', () => {
test('should detect localStorage availability', () => {
const available = StorageUtil.isLocalStorageAvailable();
expect(typeof available).toBe('boolean');
});
test('should store and retrieve items', () => {
const key = 'testKey';
const value = 'testValue';
const stored = StorageUtil.setItem(key, value);
expect(stored).toBe(true);
const retrieved = StorageUtil.getItem(key);
expect(retrieved).toBe(value);
});
test('should handle quota exceeded errors', () => {
// Test quota exceeded scenario
});
});
2. Test Fallback Storage
test('should use fallback storage when localStorage fails', () => {
// Mock localStorage failure and test fallback
});
Alternative Solutions
1. Use Session Storage
// ✅ Use sessionStorage for temporary tokens
sessionStorage.setItem('tempToken', token);
2. Use Cookies
// ✅ Use HttpOnly cookies for sensitive tokens
// More secure than localStorage for sensitive data
3. Memory Storage for Temporary Use
// ✅ Use memory storage as last resort
// Tokens will be lost on page refresh
Migration Checklist
- Check localStorage availability before storing tokens
- Handle QuotaExceededError exceptions
- Implement fallback storage methods
- Test in different browsers and contexts
- Verify storage works in incognito/private mode
- Check browser security settings compatibility
- Implement proper error handling and notifications
- Monitor storage usage and implement cleanup
Conclusion
The ‘Token not stored in localStorage’ error is a common but solvable issue that affects many web applications. By following the solutions provided in this guide—checking storage availability, handling quota exceeded errors, implementing fallback storage methods, and considering different execution contexts—you can ensure your authentication system works reliably across different browsers and environments.
The key is to implement robust error handling, provide fallback mechanisms, and always verify that storage operations succeed. With proper token storage implementation, your applications will maintain secure and persistent authentication while providing a smooth user experience.
Remember to test your implementation thoroughly across different browsers and environments, handle storage limitations appropriately, implement proper security measures, and provide user feedback when storage operations fail to ensure your authentication system is both functional and reliable.
Related Articles
Fix: 401 Unauthorized Error - Complete Guide to Authentication Issues
Complete guide to fix 401 Unauthorized errors. Learn how to resolve authentication issues with practical solutions, token management, and best practices for secure API communication.
Fix: JWT token invalid or expired error - Complete Guide
Complete guide to fix JWT token invalid or expired errors. Learn how to handle JWT authentication issues with practical solutions, token refresh strategies, and best practices for secure applications.
How to Fix: API Key Not Working error - Full Tutorial
Complete guide to fix API key not working errors. Learn how to resolve authentication issues with practical solutions, key management, and best practices for secure API communication.