search
Tutorials star Featured

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.

person By Gautam Sharma
calendar_today January 8, 2026
schedule 22 min read
localStorage Token Authentication Error Frontend Security JavaScript

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 quota
  • SecurityError: The operation is insecure
  • localStorage is not defined
  • Cannot set property of undefined
  • Storage 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.

Gautam Sharma

About Gautam Sharma

Full-stack developer and tech blogger sharing coding tutorials and best practices

Related Articles

Tutorials

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.

January 8, 2026
Tutorials

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.

January 8, 2026
Tutorials

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.

January 8, 2026