Compare commits
4 Commits
82e8afc215
...
15128bfd50
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15128bfd50 | ||
|
|
723e25e1a7 | ||
|
|
6e493e9bc7 | ||
|
|
a195fa9231 |
@@ -557,18 +557,37 @@ function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback route requires authentication - handled by CallbackPage component
|
if (isCallbackRoute) {
|
||||||
if (isCallbackRoute && isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
|
return (
|
||||||
|
<ThemeProvider>
|
||||||
|
<React.Suspense fallback={
|
||||||
|
<div className="flex items-center justify-center min-h-screen">
|
||||||
|
<div className="text-lg">Processing login...</div>
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
{mobileMode ? <CallbackMobileScreen /> : <CallbackPage />}
|
||||||
|
</React.Suspense>
|
||||||
|
<DebugInfo />
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (mobileMode) {
|
||||||
|
return (
|
||||||
|
<ThemeProvider>
|
||||||
|
<Layout mobileMode={true}>
|
||||||
|
<div className="flex items-center justify-center h-64">
|
||||||
|
<div className="text-slate-500">Processing login...</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<React.Suspense fallback={
|
<div className="flex items-center justify-center min-h-screen">
|
||||||
<div className="flex items-center justify-center min-h-screen">
|
<div className="text-lg">Processing login...</div>
|
||||||
<div className="text-lg">Processing login...</div>
|
</div>
|
||||||
</div>
|
|
||||||
}>
|
|
||||||
{mobileMode ? <CallbackMobileScreen /> : <CallbackPage />}
|
|
||||||
</React.Suspense>
|
|
||||||
<DebugInfo />
|
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,8 +57,9 @@ export const Auth0Provider: React.FC<Auth0ProviderProps> = ({ children }) => {
|
|||||||
|
|
||||||
// Component to inject token into API client with mobile support
|
// Component to inject token into API client with mobile support
|
||||||
const TokenInjector: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
const TokenInjector: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
const { getAccessTokenSilently, isAuthenticated, isLoading, user } = useAuth0();
|
const { getAccessTokenSilently, isAuthenticated, isLoading, user, logout } = useAuth0();
|
||||||
const [retryCount, setRetryCount] = React.useState(0);
|
const [retryCount, setRetryCount] = React.useState(0);
|
||||||
|
const validatingRef = React.useRef(false);
|
||||||
|
|
||||||
// Basic component loading debug
|
// Basic component loading debug
|
||||||
console.log('[TokenInjector] Component loaded');
|
console.log('[TokenInjector] Component loaded');
|
||||||
@@ -116,6 +117,31 @@ const TokenInjector: React.FC<{ children: React.ReactNode }> = ({ children }) =>
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Prevent stale session state when cached token is no longer valid
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!isAuthenticated || isLoading || validatingRef.current) return;
|
||||||
|
|
||||||
|
const validateToken = async () => {
|
||||||
|
validatingRef.current = true;
|
||||||
|
try {
|
||||||
|
await getAccessTokenSilently({ cacheMode: 'off', timeoutInSeconds: 10 });
|
||||||
|
} catch (error: any) {
|
||||||
|
const errorType = error?.error || error?.message || '';
|
||||||
|
if (errorType.includes('login_required') || errorType.includes('consent_required') ||
|
||||||
|
errorType.includes('invalid_grant')) {
|
||||||
|
console.warn('[Auth] Stale token detected, clearing auth state');
|
||||||
|
const { indexedDBStorage } = await import('../utils/indexeddb-storage');
|
||||||
|
await indexedDBStorage.clearAll();
|
||||||
|
logout({ openUrl: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
validatingRef.current = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
validateToken();
|
||||||
|
}, [isAuthenticated, isLoading, getAccessTokenSilently, logout]);
|
||||||
|
|
||||||
// Force authentication check for devices when user seems logged in but isAuthenticated is false
|
// Force authentication check for devices when user seems logged in but isAuthenticated is false
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||||
|
|||||||
@@ -157,6 +157,23 @@ class IndexedDBStorage implements StorageAdapter, Auth0Cache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clearAll(): Promise<void> {
|
||||||
|
await this.initPromise;
|
||||||
|
if (!this.db) {
|
||||||
|
this.memoryCache.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tx = this.db.transaction(this.storeName, 'readwrite');
|
||||||
|
tx.objectStore(this.storeName).clear();
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
tx.oncomplete = () => {
|
||||||
|
this.memoryCache.clear();
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
tx.onerror = () => reject(tx.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
key(index: number): string | null {
|
key(index: number): string | null {
|
||||||
const keys = Array.from(this.memoryCache.keys());
|
const keys = Array.from(this.memoryCache.keys());
|
||||||
return keys[index] || null;
|
return keys[index] || null;
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import { FeaturesGrid } from './HomePage/FeaturesGrid';
|
|||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
export const HomePage = () => {
|
export const HomePage = () => {
|
||||||
const { loginWithRedirect, isAuthenticated } = useAuth0();
|
const { loginWithRedirect, isAuthenticated, logout } = useAuth0();
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
|
const [sessionCleared, setSessionCleared] = useState(false);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -41,6 +42,22 @@ export const HomePage = () => {
|
|||||||
navigate('/signup');
|
navigate('/signup');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClearSession = async () => {
|
||||||
|
try {
|
||||||
|
const { indexedDBStorage } = await import('../core/utils/indexeddb-storage');
|
||||||
|
await indexedDBStorage.clearAll();
|
||||||
|
Object.keys(localStorage).forEach(key => {
|
||||||
|
if (key.startsWith('@@auth0')) localStorage.removeItem(key);
|
||||||
|
});
|
||||||
|
logout({ openUrl: false });
|
||||||
|
setSessionCleared(true);
|
||||||
|
setTimeout(() => setSessionCleared(false), 3000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[HomePage] Failed to clear session:', error);
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-nero text-avus">
|
<div className="min-h-screen bg-nero text-avus">
|
||||||
{/* Navigation Bar */}
|
{/* Navigation Bar */}
|
||||||
@@ -84,6 +101,12 @@ export const HomePage = () => {
|
|||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleClearSession}
|
||||||
|
className="text-white/40 hover:text-white/70 text-xs transition-colors min-h-[44px] min-w-[44px] flex items-center"
|
||||||
|
>
|
||||||
|
{sessionCleared ? 'Session cleared' : 'Trouble logging in?'}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Mobile Menu Button */}
|
{/* Mobile Menu Button */}
|
||||||
@@ -149,6 +172,12 @@ export const HomePage = () => {
|
|||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleClearSession}
|
||||||
|
className="w-full text-white/40 hover:text-white/70 text-xs py-2 min-h-[44px] transition-colors"
|
||||||
|
>
|
||||||
|
{sessionCleared ? 'Session cleared' : 'Trouble logging in?'}
|
||||||
|
</button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user