Files
maternal-app/maternal-web/lib/utils/tokenStorage.ts
Andrei d0b78181a3
Some checks failed
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
fix: Comprehensive authentication and UI fixes
Authentication & Token Management:
- Add deviceId to token refresh flow (backend requires both refreshToken and deviceId)
- Fix React Strict Mode token clearing race condition with retry logic
- Improve AuthContext to handle all token state combinations properly
- Store deviceId in localStorage alongside tokens

UI/UX Improvements:
- Remove deprecated legacyBehavior from Next.js Link components
- Update primary theme color to WCAG AA compliant #7c3aed
- Fix nested button error in TabBar voice navigation
- Fix invalid Tabs value error in DynamicChildDashboard

Multi-Child Dashboard:
- Load all children into Redux store properly
- Fetch metrics for all children, not just selected one
- Remove mock data to prevent unauthorized API calls

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-05 16:10:11 +00:00

127 lines
2.8 KiB
TypeScript

/**
* Safe token storage utilities that work with both SSR and client-side rendering
*/
export const tokenStorage = {
/**
* Get access token from storage
*/
getAccessToken: (): string | null => {
if (typeof window === 'undefined') {
return null;
}
try {
return localStorage.getItem('accessToken');
} catch (error) {
console.error('Error reading accessToken:', error);
return null;
}
},
/**
* Get refresh token from storage
*/
getRefreshToken: (): string | null => {
if (typeof window === 'undefined') {
return null;
}
try {
return localStorage.getItem('refreshToken');
} catch (error) {
console.error('Error reading refreshToken:', error);
return null;
}
},
/**
* Set access token in storage
*/
setAccessToken: (token: string): void => {
if (typeof window === 'undefined') {
return;
}
try {
localStorage.setItem('accessToken', token);
} catch (error) {
console.error('Error setting accessToken:', error);
}
},
/**
* Set refresh token in storage
*/
setRefreshToken: (token: string): void => {
if (typeof window === 'undefined') {
return;
}
try {
localStorage.setItem('refreshToken', token);
} catch (error) {
console.error('Error setting refreshToken:', error);
}
},
/**
* Get device ID from storage
*/
getDeviceId: (): string | null => {
if (typeof window === 'undefined') {
return null;
}
try {
return localStorage.getItem('deviceId');
} catch (error) {
console.error('Error reading deviceId:', error);
return null;
}
},
/**
* Set device ID in storage
*/
setDeviceId: (deviceId: string): void => {
if (typeof window === 'undefined') {
return;
}
try {
localStorage.setItem('deviceId', deviceId);
} catch (error) {
console.error('Error setting deviceId:', error);
}
},
/**
* Set both tokens at once
*/
setTokens: (accessToken: string, refreshToken: string, deviceId?: string): void => {
tokenStorage.setAccessToken(accessToken);
tokenStorage.setRefreshToken(refreshToken);
if (deviceId) {
tokenStorage.setDeviceId(deviceId);
}
},
/**
* Clear all tokens from storage
*/
clearTokens: (): void => {
if (typeof window === 'undefined') {
return;
}
try {
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
localStorage.removeItem('deviceId');
} catch (error) {
console.error('Error clearing tokens:', error);
}
},
/**
* Check if user has valid tokens
*/
hasTokens: (): boolean => {
return !!(tokenStorage.getAccessToken() && tokenStorage.getRefreshToken());
},
};