feat: frontend JWT handling, rate limiting [H-02], CORS config
Frontend: - Creiez frontend/lib/auth.ts cu saveToken, getToken, getAuthHeader, clearAuth - Modific api.ts: axiosInstance cu interceptor Bearer token + 401 → /login redirect - Modific login page: salveaza JWT token din response Backend: - [H-02] Integrez slowapi rate limiting: 10 req/minute pe /items/extract-label - [M-01] CORS: ALLOWED_ORIGINS din env (dev fallback: localhost:3000, localhost:3002) - [C-01] JWT_SECRET_KEY din env (dev fallback: ephemeral key) docker-compose.yml: - Adaug ALLOWED_ORIGINS env var (dev: localhost) - Adaug JWT_SECRET_KEY env var cu fallback warning Status: - ✅ JWT backend: complet - ✅ JWT frontend: token save + attach + 401 handling - ✅ Rate limiting: 10/min pe extract-label - ✅ CORS: configurable via env Gata pentru dev + testing local. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
79
frontend/lib/auth.ts
Normal file
79
frontend/lib/auth.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* [C-01] JWT Authentication Utilities
|
||||
* Handle token storage, retrieval, and expiration
|
||||
*/
|
||||
|
||||
const TOKEN_KEY = 'inventory_token';
|
||||
const USER_KEY = 'inventory_user';
|
||||
|
||||
export interface AuthToken {
|
||||
access_token: string;
|
||||
token_type: string;
|
||||
user_id: number;
|
||||
username: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface AuthUser {
|
||||
id: number;
|
||||
username: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save JWT token to localStorage
|
||||
*/
|
||||
export const saveToken = (token: AuthToken): void => {
|
||||
localStorage.setItem(TOKEN_KEY, token.access_token);
|
||||
localStorage.setItem(USER_KEY, JSON.stringify({
|
||||
id: token.user_id,
|
||||
username: token.username,
|
||||
role: token.role
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get JWT token from localStorage
|
||||
*/
|
||||
export const getToken = (): string | null => {
|
||||
if (typeof window === 'undefined') return null;
|
||||
return localStorage.getItem(TOKEN_KEY);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get current user info from localStorage
|
||||
*/
|
||||
export const getCurrentUser = (): AuthUser | null => {
|
||||
if (typeof window === 'undefined') return null;
|
||||
const userJson = localStorage.getItem(USER_KEY);
|
||||
if (!userJson) return null;
|
||||
try {
|
||||
return JSON.parse(userJson);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if user is authenticated (token exists)
|
||||
*/
|
||||
export const isAuthenticated = (): boolean => {
|
||||
return !!getToken();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear token and user data (logout)
|
||||
*/
|
||||
export const clearAuth = (): void => {
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
localStorage.removeItem(USER_KEY);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get Bearer token for API requests
|
||||
*/
|
||||
export const getAuthHeader = (): { Authorization: string } | {} => {
|
||||
const token = getToken();
|
||||
if (!token) return {};
|
||||
return { Authorization: `Bearer ${token}` };
|
||||
};
|
||||
Reference in New Issue
Block a user