Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
"embla-carousel-react": "^8.6.0",
"i18next": "^25.5.3",
"input-otp": "^1.4.2",
"js-cookie": "^3.0.5",
"lucide-react": "^0.544.0",
"next-themes": "^0.4.6",
"nuqs": "^2.7.0",
Expand All @@ -76,7 +75,6 @@
"@tailwindcss/vite": "^4.1.14",
"@tanstack/react-router-devtools": "^1.132.33",
"@tanstack/router-plugin": "^1.132.33",
"@types/js-cookie": "^3.0.6",
"@types/node": "^24.6.2",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
Expand Down
40 changes: 10 additions & 30 deletions apps/web/src/lib/auth-storage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Cookies from 'js-cookie';
import { UserProfile } from '@/types';

// Auth storage keys - centralized constants
Expand All @@ -8,60 +7,44 @@ export const AUTH_KEYS = {
USER: 'user',
} as const;

// Cookie options
const COOKIE_OPTIONS: Cookies.CookieAttributes = {
path: '/',
sameSite: 'strict',
secure: import.meta.env.PROD, // Only secure in production
};

const ACCESS_TOKEN_EXPIRY = 7; // 7 days
const REFRESH_TOKEN_EXPIRY = 30; // 30 days

/**
* Token storage utilities using cookies
* Token storage utilities using localStorage
*/
export const tokenStorage = {
// Get access token
getAccessToken: (): string | null => {
return Cookies.get(AUTH_KEYS.ACCESS_TOKEN) || null;
return localStorage.getItem(AUTH_KEYS.ACCESS_TOKEN);
},

// Set access token
setAccessToken: (token: string): void => {
Cookies.set(AUTH_KEYS.ACCESS_TOKEN, token, {
...COOKIE_OPTIONS,
expires: ACCESS_TOKEN_EXPIRY,
});
localStorage.setItem(AUTH_KEYS.ACCESS_TOKEN, token);
},
Comment on lines 13 to 22
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Access and refresh tokens are stored in localStorage, increasing XSS exposure risk. Consider HttpOnly secure cookies or an in-memory strategy plus a refresh rotation to mitigate token theft.

Copilot uses AI. Check for mistakes.

// Remove access token
removeAccessToken: (): void => {
Cookies.remove(AUTH_KEYS.ACCESS_TOKEN, { path: '/' });
localStorage.removeItem(AUTH_KEYS.ACCESS_TOKEN);
},

// Get refresh token
getRefreshToken: (): string | null => {
return Cookies.get(AUTH_KEYS.REFRESH_TOKEN) || null;
return localStorage.getItem(AUTH_KEYS.REFRESH_TOKEN);
},

// Set refresh token
setRefreshToken: (token: string): void => {
Cookies.set(AUTH_KEYS.REFRESH_TOKEN, token, {
...COOKIE_OPTIONS,
expires: REFRESH_TOKEN_EXPIRY,
});
localStorage.setItem(AUTH_KEYS.REFRESH_TOKEN, token);
},

// Remove refresh token
removeRefreshToken: (): void => {
Cookies.remove(AUTH_KEYS.REFRESH_TOKEN, { path: '/' });
localStorage.removeItem(AUTH_KEYS.REFRESH_TOKEN);
},

// Get user profile
getUser: (): UserProfile | null => {
try {
const userStr = Cookies.get(AUTH_KEYS.USER);
const userStr = localStorage.getItem(AUTH_KEYS.USER);
return userStr ? JSON.parse(userStr) : null;
} catch {
return null;
Expand All @@ -70,15 +53,12 @@ export const tokenStorage = {

// Set user profile
setUser: (user: UserProfile): void => {
Cookies.set(AUTH_KEYS.USER, JSON.stringify(user), {
...COOKIE_OPTIONS,
expires: ACCESS_TOKEN_EXPIRY,
});
localStorage.setItem(AUTH_KEYS.USER, JSON.stringify(user));
},

// Remove user profile
removeUser: (): void => {
Cookies.remove(AUTH_KEYS.USER, { path: '/' });
localStorage.removeItem(AUTH_KEYS.USER);
},

// Set all auth data
Expand Down
15 changes: 0 additions & 15 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.