auth.ts - Authentication API Module¶
Purpose¶
The auth.ts
file provides comprehensive authentication and authorization functionality for the ReViewPoint application. This module serves as the primary interface between the frontend and backend authentication services, offering secure user registration, login, logout, token management, and password reset capabilities. It mirrors the backend authentication endpoints while providing a consistent TypeScript interface with robust error handling and security features.
Key Features¶
Core Authentication Operations¶
- User Registration: Secure account creation with validation
- User Login: Credential-based authentication with JWT tokens
- User Logout: Secure session termination and token invalidation
- Token Management: Automatic token refresh and session maintenance
- Password Recovery: Complete password reset workflow with email verification
Security Features¶
- JWT Token-Based Authentication: Secure, stateless authentication
- Automatic Token Refresh: Seamless session extension without user intervention
- Secure Password Handling: Client-side validation and secure transmission
- Session Management: Complete session lifecycle management
- Error Handling: Comprehensive error handling with detailed logging
API Functions¶
User Registration (register
)¶
Creates a new user account with comprehensive validation and security features.
Parameters:
userData: AuthRegisterRequest
- User registration data including email, name, and password
Returns:
Promise<AuthRegisterResponse>
- Registration response with user data and authentication tokens
Example Usage:
import { authApi } from "@/lib/api";
async function handleUserRegistration() {
try {
const userData = {
email: "john.doe@example.com",
name: "John Doe",
password: "SecurePassword123!",
};
const response = await authApi.register(userData);
console.log("Registration successful:", {
user: response.user,
hasToken: !!response.access_token,
});
// Store authentication tokens
localStorage.setItem("access_token", response.access_token);
localStorage.setItem("refresh_token", response.refresh_token);
localStorage.setItem("token_type", response.token_type);
// Redirect to dashboard or main application
window.location.href = "/dashboard";
return response;
} catch (error) {
console.error("Registration failed:", error.message);
// Handle specific registration errors
if (error.message.includes("email already exists")) {
showError(
"This email is already registered. Please use a different email or try logging in.",
);
} else if (error.message.includes("password")) {
showError(
"Password does not meet security requirements. Please choose a stronger password.",
);
} else if (error.message.includes("validation")) {
showError("Please check your input and try again.");
} else {
showError("Registration failed. Please try again later.");
}
throw error;
}
}
// Registration with validation
async function registrationWithValidation(formData: {
email: string;
name: string;
password: string;
confirmPassword: string;
}) {
// Client-side validation
const errors = validateRegistrationData(formData);
if (errors.length > 0) {
throw new Error(`Validation failed: ${errors.join(", ")}`);
}
// Prepare registration data
const userData = {
email: formData.email.toLowerCase().trim(),
name: formData.name.trim(),
password: formData.password,
};
return await authApi.register(userData);
}
function validateRegistrationData(data: {
email: string;
name: string;
password: string;
confirmPassword: string;
}): string[] {
const errors: string[] = [];
// Email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(data.email)) {
errors.push("Please enter a valid email address");
}
// Name validation
if (data.name.length < 2) {
errors.push("Name must be at least 2 characters long");
}
// Password validation
if (data.password.length < 8) {
errors.push("Password must be at least 8 characters long");
}
if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(data.password)) {
errors.push("Password must contain uppercase, lowercase, and number");
}
// Confirm password
if (data.password !== data.confirmPassword) {
errors.push("Passwords do not match");
}
return errors;
}
User Authentication (login
)¶
Authenticates existing users and establishes secure sessions.
Parameters:
loginData: AuthLoginRequest
- Login credentials (email and password)
Returns:
Promise<AuthLoginResponse>
- Authentication response with user data and tokens
Example Usage:
async function handleUserLogin() {
try {
const credentials = {
email: "john.doe@example.com",
password: "SecurePassword123!",
};
const response = await authApi.login(credentials);
console.log("Login successful:", {
user: response.user,
tokenType: response.token_type,
});
// Store authentication data
storeAuthenticationData(response);
// Update application state
updateUserSession(response.user);
// Redirect to intended destination
const returnUrl = sessionStorage.getItem("returnUrl") || "/dashboard";
sessionStorage.removeItem("returnUrl");
window.location.href = returnUrl;
return response;
} catch (error) {
console.error("Login failed:", error.message);
handleLoginError(error);
throw error;
}
}
function storeAuthenticationData(authResponse: AuthLoginResponse) {
// Store tokens securely
localStorage.setItem("access_token", authResponse.access_token);
localStorage.setItem("refresh_token", authResponse.refresh_token);
localStorage.setItem("token_type", authResponse.token_type);
// Store user data (excluding sensitive info)
const userData = {
id: authResponse.user.id,
email: authResponse.user.email,
name: authResponse.user.name,
role: authResponse.user.role,
};
localStorage.setItem("user_data", JSON.stringify(userData));
// Set session timestamp
localStorage.setItem("login_timestamp", new Date().toISOString());
}
function handleLoginError(error: Error) {
if (error.message.includes("invalid credentials")) {
showError(
"Invalid email or password. Please check your credentials and try again.",
);
} else if (error.message.includes("account locked")) {
showError(
"Your account has been temporarily locked due to too many failed login attempts.",
);
} else if (error.message.includes("email not verified")) {
showError("Please verify your email address before logging in.");
} else if (error.message.includes("network")) {
showError(
"Connection error. Please check your internet connection and try again.",
);
} else {
showError("Login failed. Please try again later.");
}
}
// Login with remember me functionality
async function loginWithRememberMe(
credentials: AuthLoginRequest,
rememberMe: boolean,
) {
const response = await authApi.login(credentials);
if (rememberMe) {
// Store tokens in localStorage for persistence
storeAuthenticationData(response);
} else {
// Store tokens in sessionStorage for session-only persistence
sessionStorage.setItem("access_token", response.access_token);
sessionStorage.setItem("refresh_token", response.refresh_token);
sessionStorage.setItem("token_type", response.token_type);
}
return response;
}
Session Management (logout
)¶
Securely terminates user sessions and clears authentication data.
Returns:
Promise<AuthLogoutResponse>
- Logout confirmation response
Example Usage:
async function handleUserLogout() {
try {
// Perform server-side logout
const response = await authApi.logout();
console.log("Logout successful:", response.message);
// Clear all authentication data
clearAuthenticationData();
// Clear application state
clearUserSession();
// Redirect to login page
window.location.href = "/login";
return response;
} catch (error) {
console.error("Logout failed:", error.message);
// Even if server logout fails, clear local data
clearAuthenticationData();
clearUserSession();
// Still redirect to login
window.location.href = "/login";
}
}
function clearAuthenticationData() {
// Remove tokens and user data
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
localStorage.removeItem("token_type");
localStorage.removeItem("user_data");
localStorage.removeItem("login_timestamp");
// Clear session storage as well
sessionStorage.removeItem("access_token");
sessionStorage.removeItem("refresh_token");
sessionStorage.removeItem("token_type");
sessionStorage.removeItem("returnUrl");
}
function clearUserSession() {
// Clear any application-specific state
// This would be customized based on your state management solution
// Example: Clear Zustand store
// useAuthStore.getState().clearUser();
// Example: Clear React Query cache
// queryClient.clear();
}
// Automatic logout on tab close
window.addEventListener("beforeunload", () => {
const rememberMe = localStorage.getItem("access_token");
if (!rememberMe) {
// Only auto-logout if not using "remember me"
authApi.logout().catch(console.error);
}
});
Token Management (refreshToken
)¶
Automatically refreshes JWT access tokens to maintain user sessions.
Parameters:
refreshToken: string
- Valid refresh token
Returns:
Promise<AuthTokenRefreshResponse>
- New access token and metadata
Example Usage:
async function handleTokenRefresh() {
try {
const storedRefreshToken =
localStorage.getItem("refresh_token") ||
sessionStorage.getItem("refresh_token");
if (!storedRefreshToken) {
throw new Error("No refresh token available");
}
const response = await authApi.refreshToken(storedRefreshToken);
console.log("Token refreshed successfully");
// Update stored tokens
const storage = localStorage.getItem("refresh_token")
? localStorage
: sessionStorage;
storage.setItem("access_token", response.access_token);
storage.setItem("token_type", response.token_type);
// Update refresh token if provided
if (response.refresh_token) {
storage.setItem("refresh_token", response.refresh_token);
}
return response;
} catch (error) {
console.error("Token refresh failed:", error.message);
// If refresh fails, user needs to log in again
clearAuthenticationData();
window.location.href = "/login";
throw error;
}
}
// Automatic token refresh interceptor
class TokenManager {
private refreshPromise: Promise<AuthTokenRefreshResponse> | null = null;
async getValidToken(): Promise<string> {
const token = this.getStoredToken();
if (!token) {
throw new Error("No access token available");
}
// Check if token is expired or expiring soon
if (this.isTokenExpired(token) || this.isTokenExpiringSoon(token)) {
return await this.refreshTokenIfNeeded();
}
return token;
}
private async refreshTokenIfNeeded(): Promise<string> {
// Prevent multiple simultaneous refresh requests
if (this.refreshPromise) {
const response = await this.refreshPromise;
return response.access_token;
}
this.refreshPromise = handleTokenRefresh();
try {
const response = await this.refreshPromise;
return response.access_token;
} finally {
this.refreshPromise = null;
}
}
private getStoredToken(): string | null {
return (
localStorage.getItem("access_token") ||
sessionStorage.getItem("access_token")
);
}
private isTokenExpired(token: string): boolean {
try {
const payload = JSON.parse(atob(token.split(".")[1]));
return payload.exp * 1000 < Date.now();
} catch {
return true; // Assume expired if we can't parse
}
}
private isTokenExpiringSoon(
token: string,
bufferMinutes: number = 5,
): boolean {
try {
const payload = JSON.parse(atob(token.split(".")[1]));
const bufferMs = bufferMinutes * 60 * 1000;
return payload.exp * 1000 < Date.now() + bufferMs;
} catch {
return true;
}
}
}
export const tokenManager = new TokenManager();
Password Recovery (forgotPassword
& resetPassword
)¶
Complete password reset workflow with email verification.
async function forgotPassword(
resetRequest: AuthPasswordResetRequest,
): Promise<AuthPasswordResetResponse>;
async function resetPassword(
resetRequest: AuthPasswordResetConfirmRequest,
): Promise<AuthPasswordResetConfirmResponse>;
Example Usage:
// Step 1: Request password reset
async function requestPasswordReset(email: string) {
try {
const response = await authApi.forgotPassword({ email });
console.log("Password reset email sent:", response.message);
showSuccess(
"Password reset instructions have been sent to your email address.",
);
return response;
} catch (error) {
console.error("Password reset request failed:", error.message);
if (error.message.includes("not found")) {
showError("No account found with this email address.");
} else if (error.message.includes("rate limit")) {
showError("Too many reset requests. Please wait before trying again.");
} else {
showError("Failed to send reset email. Please try again later.");
}
throw error;
}
}
// Step 2: Reset password with token
async function confirmPasswordReset(token: string, newPassword: string) {
try {
const resetRequest = {
token,
new_password: newPassword,
};
const response = await authApi.resetPassword(resetRequest);
console.log("Password reset successful:", response.message);
showSuccess(
"Your password has been reset successfully. Please log in with your new password.",
);
// Redirect to login page
setTimeout(() => {
window.location.href = "/login";
}, 2000);
return response;
} catch (error) {
console.error("Password reset failed:", error.message);
if (error.message.includes("invalid token")) {
showError(
"Invalid or expired reset token. Please request a new password reset.",
);
} else if (error.message.includes("password")) {
showError("Password does not meet security requirements.");
} else {
showError("Failed to reset password. Please try again.");
}
throw error;
}
}
// Complete password reset flow component
class PasswordResetFlow {
async handleForgotPassword(email: string) {
// Validate email format
if (!this.isValidEmail(email)) {
throw new Error("Please enter a valid email address");
}
return await requestPasswordReset(email);
}
async handlePasswordReset(
token: string,
password: string,
confirmPassword: string,
) {
// Validate new password
const passwordErrors = this.validatePassword(password, confirmPassword);
if (passwordErrors.length > 0) {
throw new Error(
`Password validation failed: ${passwordErrors.join(", ")}`,
);
}
return await confirmPasswordReset(token, password);
}
private isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
private validatePassword(
password: string,
confirmPassword: string,
): string[] {
const errors: string[] = [];
if (password.length < 8) {
errors.push("Password must be at least 8 characters long");
}
if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(password)) {
errors.push("Password must contain uppercase, lowercase, and number");
}
if (password !== confirmPassword) {
errors.push("Passwords do not match");
}
return errors;
}
}
User Profile Management (getCurrentUser
)¶
Retrieves current user profile information and session details.
Returns:
Promise<User>
- Current user profile data
Example Usage:
async function loadUserProfile() {
try {
const user = await authApi.getCurrentUser();
console.log("User profile loaded:", {
id: user.id,
email: user.email,
name: user.name,
role: user.role,
});
// Update UI with user information
updateUserInterface(user);
// Store user data locally
localStorage.setItem("user_data", JSON.stringify(user));
return user;
} catch (error) {
console.error("Failed to load user profile:", error.message);
if (error.message.includes("unauthorized")) {
// Token might be expired or invalid
console.log("User not authenticated, redirecting to login");
clearAuthenticationData();
window.location.href = "/login";
} else {
showError("Failed to load user profile. Please refresh the page.");
}
throw error;
}
}
function updateUserInterface(user: User) {
// Update navigation with user name
const userNameElement = document.getElementById("user-name");
if (userNameElement) {
userNameElement.textContent = user.name;
}
// Update avatar or profile picture
const avatarElement = document.getElementById("user-avatar");
if (avatarElement) {
avatarElement.src = user.avatar_url || generateAvatarUrl(user.name);
}
// Show/hide admin features based on role
const adminElements = document.querySelectorAll(".admin-only");
adminElements.forEach((element) => {
if (user.role === "admin") {
element.style.display = "block";
} else {
element.style.display = "none";
}
});
}
function generateAvatarUrl(name: string): string {
// Generate a default avatar URL based on user name
const initials = name
.split(" ")
.map((word) => word[0])
.join("")
.toUpperCase();
return `https://ui-avatars.com/api/?name=${encodeURIComponent(initials)}&background=0D8ABC&color=fff`;
}
// Automatic profile refresh
async function setupProfileRefresh() {
// Refresh user profile every 30 minutes
setInterval(
async () => {
try {
await loadUserProfile();
console.log("User profile refreshed");
} catch (error) {
console.error("Profile refresh failed:", error.message);
}
},
30 * 60 * 1000,
); // 30 minutes
}
Type Definitions¶
Authentication Request Types¶
interface AuthRegisterRequest {
email: string; // User email address (required)
name?: string; // User full name (optional, defaults to email prefix)
password: string; // User password (required)
}
interface AuthLoginRequest {
email: string; // User email address
password: string; // User password
}
interface AuthPasswordResetRequest {
email: string; // Email address for password reset
}
interface AuthPasswordResetConfirmRequest {
token: string; // Password reset token from email
new_password: string; // New password to set
}
Authentication Response Types¶
interface AuthRegisterResponse {
user: User; // User profile data
access_token: string; // JWT access token
refresh_token: string; // JWT refresh token
token_type: string; // Token type (always "bearer")
}
interface AuthLoginResponse {
user: User; // User profile data
access_token: string; // JWT access token
refresh_token: string; // JWT refresh token
token_type: string; // Token type (always "bearer")
}
interface AuthLogoutResponse {
message: string; // Logout confirmation message
}
interface AuthTokenRefreshResponse {
access_token: string; // New JWT access token
refresh_token?: string; // New refresh token (if rotated)
token_type: string; // Token type (always "bearer")
}
interface AuthPasswordResetResponse {
message: string; // Reset request confirmation
}
interface AuthPasswordResetConfirmResponse {
message: string; // Password reset confirmation
}
User Profile Type¶
interface User {
id: string; // Unique user identifier
email: string; // User email address
name: string; // User full name
role: string; // User role (user, admin, etc.)
avatar_url?: string; // Profile picture URL (optional)
created_at: string; // Account creation timestamp
updated_at: string; // Last profile update timestamp
email_verified: boolean; // Email verification status
is_active: boolean; // Account active status
}
Advanced Usage Examples¶
Complete Authentication Service¶
import { authApi } from "@/lib/api";
class AuthenticationService {
private tokenManager = new TokenManager();
private eventEmitter = new EventTarget();
// Complete registration flow
async register(userData: {
email: string;
name: string;
password: string;
confirmPassword: string;
}) {
// Validate input
const errors = this.validateRegistrationData(userData);
if (errors.length > 0) {
throw new Error(`Validation failed: ${errors.join(", ")}`);
}
try {
const response = await authApi.register({
email: userData.email,
name: userData.name,
password: userData.password,
});
// Store authentication data
this.storeAuthData(response);
// Emit registration event
this.emit("userRegistered", response.user);
return response;
} catch (error) {
this.emit("registrationFailed", error);
throw error;
}
}
// Complete login flow
async login(credentials: AuthLoginRequest, rememberMe: boolean = false) {
try {
const response = await authApi.login(credentials);
// Store authentication data
this.storeAuthData(response, rememberMe);
// Load full user profile
const user = await this.loadUserProfile();
// Emit login event
this.emit("userLoggedIn", user);
return response;
} catch (error) {
this.emit("loginFailed", error);
throw error;
}
}
// Complete logout flow
async logout() {
try {
await authApi.logout();
} catch (error) {
console.error("Server logout failed:", error.message);
} finally {
// Always clear local data
this.clearAuthData();
this.emit("userLoggedOut");
}
}
// Check authentication status
async isAuthenticated(): Promise<boolean> {
try {
const token = await this.tokenManager.getValidToken();
return !!token;
} catch {
return false;
}
}
// Get current user with caching
async getCurrentUser(): Promise<User | null> {
try {
if (!(await this.isAuthenticated())) {
return null;
}
return await authApi.getCurrentUser();
} catch (error) {
console.error("Failed to get current user:", error.message);
return null;
}
}
// Complete password reset flow
async requestPasswordReset(email: string) {
try {
const response = await authApi.forgotPassword({ email });
this.emit("passwordResetRequested", { email });
return response;
} catch (error) {
this.emit("passwordResetFailed", error);
throw error;
}
}
async confirmPasswordReset(token: string, newPassword: string) {
try {
const response = await authApi.resetPassword({
token,
new_password: newPassword,
});
this.emit("passwordResetConfirmed");
return response;
} catch (error) {
this.emit("passwordResetConfirmationFailed", error);
throw error;
}
}
// Utility methods
private storeAuthData(
authResponse: AuthLoginResponse | AuthRegisterResponse,
persistent: boolean = true,
) {
const storage = persistent ? localStorage : sessionStorage;
storage.setItem("access_token", authResponse.access_token);
storage.setItem("refresh_token", authResponse.refresh_token);
storage.setItem("token_type", authResponse.token_type);
const userData = {
id: authResponse.user.id,
email: authResponse.user.email,
name: authResponse.user.name,
role: authResponse.user.role,
};
storage.setItem("user_data", JSON.stringify(userData));
}
private clearAuthData() {
// Clear localStorage
["access_token", "refresh_token", "token_type", "user_data"].forEach(
(key) => {
localStorage.removeItem(key);
sessionStorage.removeItem(key);
},
);
}
private async loadUserProfile(): Promise<User> {
return await authApi.getCurrentUser();
}
private validateRegistrationData(data: {
email: string;
name: string;
password: string;
confirmPassword: string;
}): string[] {
const errors: string[] = [];
// Email validation
if (!this.isValidEmail(data.email)) {
errors.push("Invalid email format");
}
// Name validation
if (data.name.length < 2) {
errors.push("Name must be at least 2 characters");
}
// Password validation
if (data.password.length < 8) {
errors.push("Password must be at least 8 characters");
}
if (data.password !== data.confirmPassword) {
errors.push("Passwords do not match");
}
return errors;
}
private isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
private emit(eventType: string, data?: any) {
this.eventEmitter.dispatchEvent(
new CustomEvent(eventType, { detail: data }),
);
}
// Event listeners
addEventListener(type: string, listener: EventListener) {
this.eventEmitter.addEventListener(type, listener);
}
removeEventListener(type: string, listener: EventListener) {
this.eventEmitter.removeEventListener(type, listener);
}
}
export const authService = new AuthenticationService();
React Hook Integration¶
import { useState, useEffect } from "react";
import { authService } from "./auth-service";
export function useAuth() {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
loadUser();
// Listen for auth events
const handleLogin = (event: CustomEvent) => {
setUser(event.detail);
setError(null);
};
const handleLogout = () => {
setUser(null);
setError(null);
};
const handleError = (event: CustomEvent) => {
setError(event.detail.message);
};
authService.addEventListener("userLoggedIn", handleLogin);
authService.addEventListener("userLoggedOut", handleLogout);
authService.addEventListener("loginFailed", handleError);
return () => {
authService.removeEventListener("userLoggedIn", handleLogin);
authService.removeEventListener("userLoggedOut", handleLogout);
authService.removeEventListener("loginFailed", handleError);
};
}, []);
async function loadUser() {
setLoading(true);
try {
const currentUser = await authService.getCurrentUser();
setUser(currentUser);
} catch (error) {
console.error("Failed to load user:", error.message);
setUser(null);
} finally {
setLoading(false);
}
}
const login = async (credentials: AuthLoginRequest, rememberMe?: boolean) => {
setLoading(true);
setError(null);
try {
await authService.login(credentials, rememberMe);
} catch (error) {
setError(error.message);
throw error;
} finally {
setLoading(false);
}
};
const register = async (userData: {
email: string;
name: string;
password: string;
confirmPassword: string;
}) => {
setLoading(true);
setError(null);
try {
await authService.register(userData);
} catch (error) {
setError(error.message);
throw error;
} finally {
setLoading(false);
}
};
const logout = async () => {
setLoading(true);
try {
await authService.logout();
} catch (error) {
console.error("Logout failed:", error.message);
} finally {
setLoading(false);
}
};
return {
user,
loading,
error,
isAuthenticated: !!user,
login,
register,
logout,
clearError: () => setError(null),
};
}
Security Considerations¶
Token Security¶
- Secure Storage: Tokens are stored in localStorage or sessionStorage based on user preference
- Automatic Refresh: Access tokens are automatically refreshed before expiration
- Token Validation: Client-side token expiration checking and validation
- Secure Transmission: All authentication requests use HTTPS in production
Password Security¶
- Client-side Validation: Password strength requirements enforced on frontend
- Secure Transmission: Passwords are never stored locally and transmitted securely
- Reset Flow: Complete password reset workflow with email verification
- Rate Limiting: Backend implements rate limiting for authentication attempts
Session Management¶
- Automatic Logout: Configurable automatic logout on inactivity
- Concurrent Sessions: Support for multiple device sessions with proper token management
- Session Monitoring: Active session tracking and management
- Secure Logout: Complete session cleanup on logout
Error Handling Patterns¶
Comprehensive Error Management¶
class AuthErrorHandler {
static handleAuthError(error: Error, context: string): string {
console.error(`Authentication error in ${context}:`, error.message);
// Network errors
if (error.message.includes("network") || error.message.includes("fetch")) {
return "Connection error. Please check your internet connection and try again.";
}
// Authentication specific errors
switch (context) {
case "register":
return this.handleRegistrationError(error);
case "login":
return this.handleLoginError(error);
case "logout":
return this.handleLogoutError(error);
case "refresh":
return this.handleRefreshError(error);
case "password-reset":
return this.handlePasswordResetError(error);
default:
return `Authentication failed: ${error.message}`;
}
}
private static handleRegistrationError(error: Error): string {
if (error.message.includes("email already exists")) {
return "This email is already registered. Please use a different email or try logging in.";
} else if (error.message.includes("password")) {
return "Password does not meet security requirements. Please choose a stronger password.";
} else if (error.message.includes("validation")) {
return "Please check your input and try again.";
}
return "Registration failed. Please try again later.";
}
private static handleLoginError(error: Error): string {
if (error.message.includes("invalid credentials")) {
return "Invalid email or password. Please check your credentials and try again.";
} else if (error.message.includes("account locked")) {
return "Your account has been temporarily locked due to too many failed login attempts.";
} else if (error.message.includes("email not verified")) {
return "Please verify your email address before logging in.";
}
return "Login failed. Please try again later.";
}
private static handleLogoutError(error: Error): string {
return "Logout completed locally. Server logout may have failed.";
}
private static handleRefreshError(error: Error): string {
return "Session expired. Please log in again.";
}
private static handlePasswordResetError(error: Error): string {
if (error.message.includes("not found")) {
return "No account found with this email address.";
} else if (error.message.includes("rate limit")) {
return "Too many reset requests. Please wait before trying again.";
} else if (error.message.includes("invalid token")) {
return "Invalid or expired reset token. Please request a new password reset.";
}
return "Password reset failed. Please try again later.";
}
}
Performance Considerations¶
Optimization Strategies¶
- Token Caching: Intelligent caching of valid tokens to minimize API calls
- Request Deduplication: Prevent multiple simultaneous token refresh requests
- Lazy Loading: Load user profile only when needed
- Background Refresh: Proactive token refresh to maintain seamless sessions
Memory Management¶
// Efficient token management
class TokenCache {
private static tokenCache: string | null = null;
private static cacheTimestamp: number = 0;
private static readonly CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
static getToken(): string | null {
const now = Date.now();
if (this.tokenCache && now - this.cacheTimestamp < this.CACHE_DURATION) {
return this.tokenCache;
}
// Cache expired, get fresh token
this.tokenCache =
localStorage.getItem("access_token") ||
sessionStorage.getItem("access_token");
this.cacheTimestamp = now;
return this.tokenCache;
}
static clearCache() {
this.tokenCache = null;
this.cacheTimestamp = 0;
}
}
Backend Integration¶
Corresponding Backend Endpoints¶
This module integrates with the following backend endpoints:
POST /api/v1/auth/register
- User registrationPOST /api/v1/auth/login
- User authenticationPOST /api/v1/auth/logout
- Session terminationPOST /api/v1/auth/refresh-token
- Token refreshGET /api/v1/auth/me
- Current user profilePOST /api/v1/auth/request-password-reset
- Password reset requestPOST /api/v1/auth/reset-password
- Password reset confirmation
Data Synchronization¶
- Type Consistency: Frontend types match backend schemas exactly
- Error Code Mapping: HTTP status codes are consistently handled
- Token Format: JWT tokens follow backend implementation exactly
- Validation Rules: Client-side validation mirrors server-side rules
Dependencies¶
External Dependencies¶
- @/logger: Application logging service for authentication tracking
- ./base: Base HTTP client providing the
request
function - ./types: TypeScript type definitions for authentication data
Internal Dependencies¶
- TypeScript: Type safety for authentication operations
- JWT Handling: Client-side JWT token parsing and validation
- Local Storage: Browser storage for session persistence
Related Files¶
- index.ts: Main API module that exports authentication functionality
- base.ts: Base HTTP client used for all requests
- types.ts: TypeScript type definitions
- users/: User management functionality
- Backend:
backend/src/api/v1/auth.py
- corresponding backend implementation
Implementation Best Practices¶
Authentication Flow¶
- Secure Token Handling: Never expose tokens in console logs or error messages
- Graceful Degradation: Handle authentication failures gracefully
- Session Persistence: Support both persistent and session-only authentication
- Automatic Recovery: Implement automatic token refresh and error recovery
User Experience¶
- Clear Feedback: Provide immediate feedback for all authentication actions
- Error Messaging: Show user-friendly error messages with recovery instructions
- Loading States: Display appropriate loading indicators during authentication
- Accessibility: Ensure authentication forms work with screen readers and keyboards