diff --git a/mnemo_cards_admin/src/api/client.ts b/mnemo_cards_admin/src/api/client.ts index 1949684..a35d965 100644 --- a/mnemo_cards_admin/src/api/client.ts +++ b/mnemo_cards_admin/src/api/client.ts @@ -1,4 +1,5 @@ import axios from 'axios' +import { useAuthStore } from '@/stores/authStore' const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'https://api.mnemo-cards.online' @@ -24,29 +25,72 @@ export const adminApiClient = axios.create({ // Request interceptor to add auth token const addAuthToken = (config: any) => { const token = localStorage.getItem('admin_token') + const isPublicAuthEndpoint = + config.url?.includes('/admin/auth/request-code') || + config.url?.includes('/admin/auth/verify-code') || + config.url?.includes('/admin/auth/code-status') + if (token && token.trim()) { // Ensure headers object exists if (!config.headers) { config.headers = {} } - config.headers.Authorization = `Bearer ${token.trim()}` + const trimmedToken = token.trim() + // Set Authorization header - axios will normalize it to lowercase for HTTP + // but we use the standard capitalization + config.headers['Authorization'] = `Bearer ${trimmedToken}` + + // Debug logging for admin endpoints + if (!isPublicAuthEndpoint && config.url?.includes('/admin/')) { + console.log('[API Client] Adding auth token to request:', config.url) + console.log('[API Client] Token exists, length:', trimmedToken.length) + console.log('[API Client] Authorization header set:', config.headers['Authorization']?.substring(0, 30) + '...') + } } else { - // Log warning if token is missing (except for public auth endpoints) - const isPublicAuthEndpoint = - config.url?.includes('/admin/auth/request-code') || - config.url?.includes('/admin/auth/verify-code') + // Log error if token is missing (except for public auth endpoints) if (!isPublicAuthEndpoint) { - console.warn('No admin token found in localStorage for request:', config.url) + console.error('[API Client] No admin token found in localStorage for request:', config.url) + console.error('[API Client] localStorage.getItem("admin_token"):', localStorage.getItem('admin_token')) + console.error('[API Client] All localStorage keys:', Object.keys(localStorage)) } } return config } +// Flag to prevent multiple logout calls +let isLoggingOut = false + // Response interceptor for error handling const handleAuthError = (error: any) => { if (error.response?.status === 401) { + // Check if we're already on login page + const currentPath = window.location.pathname + const isPublicAuthEndpoint = + error.config?.url?.includes('/admin/auth/request-code') || + error.config?.url?.includes('/admin/auth/verify-code') || + error.config?.url?.includes('/admin/auth/code-status') + + // Don't logout for public auth endpoints or if already on login page + if (currentPath === '/login' || isPublicAuthEndpoint) { + return Promise.reject(error) + } + + // Prevent multiple logout calls + if (isLoggingOut) { + return Promise.reject(error) + } + + isLoggingOut = true + + // Remove token and update auth store + // React Router will automatically redirect to /login when isAuthenticated becomes false localStorage.removeItem('admin_token') - window.location.href = '/login' + useAuthStore.getState().logout() + + // Reset flag after a short delay to allow React Router to handle redirect + setTimeout(() => { + isLoggingOut = false + }, 1000) } return Promise.reject(error) } diff --git a/mnemo_cards_admin/src/stores/authStore.ts b/mnemo_cards_admin/src/stores/authStore.ts index ffa3892..22a2ee4 100644 --- a/mnemo_cards_admin/src/stores/authStore.ts +++ b/mnemo_cards_admin/src/stores/authStore.ts @@ -31,7 +31,11 @@ export const useAuthStore = create()( // Actions login: (token: string, user: UserDto) => { + console.log('[AuthStore] Saving token to localStorage, length:', token.length) localStorage.setItem('admin_token', token) + // Verify it was saved + const savedToken = localStorage.getItem('admin_token') + console.log('[AuthStore] Token saved, verification:', savedToken ? `Token exists, length: ${savedToken.length}` : 'Token NOT found!') set({ user, token, diff --git a/mnemo_cards_backend/lib/api/v2/authorize_v2.dart b/mnemo_cards_backend/lib/api/v2/authorize_v2.dart index 17c0013..485e101 100644 --- a/mnemo_cards_backend/lib/api/v2/authorize_v2.dart +++ b/mnemo_cards_backend/lib/api/v2/authorize_v2.dart @@ -86,7 +86,17 @@ Middleware authorizeV2(UserManager userManager, JwtService jwtService) { ); // Extract Bearer token from Authorization header + // Shelf normalizes headers to lowercase, so we check 'authorization' final authHeader = request.headers['authorization']; + + // Debug logging for admin endpoints + if (normalizedPath.startsWith('/admin/')) { + print('[AUTH DEBUG] Path: $normalizedPath'); + print('[AUTH DEBUG] All headers: ${request.headers}'); + print('[AUTH DEBUG] Authorization header: $authHeader'); + print('[AUTH DEBUG] Authorization header type: ${authHeader.runtimeType}'); + } + final hasBearerToken = authHeader != null && authHeader.startsWith('Bearer ');