mirror of
https://github.com/stackblitz/bolt.new
synced 2025-06-26 18:17:50 +00:00
- Enhanced encryption system with AES-GCM and PBKDF2 - Added secure authentication system with session management - Implemented security middleware with headers and rate limiting - Created secure storage utilities for sensitive data - Added login system with brute force protection - Implemented security components (password strength, 2FA) - Added security utility functions for threat detection Copyright (c) 2024 Ervin Remus Radosavlevici
110 lines
2.8 KiB
TypeScript
110 lines
2.8 KiB
TypeScript
import { createCookieSessionStorage, redirect } from '@remix-run/cloudflare';
|
|
import { createScopedLogger } from '~/utils/logger';
|
|
|
|
const logger = createScopedLogger('Auth');
|
|
|
|
// Session duration (24 hours)
|
|
const SESSION_EXPIRY = 60 * 60 * 24;
|
|
|
|
// Create session storage
|
|
export const sessionStorage = createCookieSessionStorage({
|
|
cookie: {
|
|
name: 'bolt_session',
|
|
httpOnly: true,
|
|
path: '/',
|
|
sameSite: 'lax',
|
|
secrets: [process.env.SESSION_SECRET || 'default-secret-change-me'],
|
|
secure: process.env.NODE_ENV === 'production',
|
|
maxAge: SESSION_EXPIRY,
|
|
},
|
|
});
|
|
|
|
// Get session from request
|
|
export async function getSession(request: Request) {
|
|
const cookie = request.headers.get('Cookie');
|
|
return sessionStorage.getSession(cookie);
|
|
}
|
|
|
|
// User session data type
|
|
export type UserSession = {
|
|
userId: string;
|
|
authenticated: boolean;
|
|
lastActivity: number;
|
|
};
|
|
|
|
// Create a new session
|
|
export async function createUserSession(userId: string, redirectTo: string) {
|
|
const session = await sessionStorage.getSession();
|
|
|
|
const userSession: UserSession = {
|
|
userId,
|
|
authenticated: true,
|
|
lastActivity: Date.now(),
|
|
};
|
|
|
|
session.set('user', userSession);
|
|
|
|
return redirect(redirectTo, {
|
|
headers: {
|
|
'Set-Cookie': await sessionStorage.commitSession(session),
|
|
},
|
|
});
|
|
}
|
|
|
|
// Get user from session
|
|
export async function getUserFromSession(request: Request): Promise<UserSession | null> {
|
|
const session = await getSession(request);
|
|
const userSession = session.get('user') as UserSession | undefined;
|
|
|
|
if (!userSession || !userSession.authenticated) {
|
|
return null;
|
|
}
|
|
|
|
// Check for session expiry (inactivity timeout)
|
|
const now = Date.now();
|
|
const inactiveTime = now - userSession.lastActivity;
|
|
if (inactiveTime > SESSION_EXPIRY * 1000) {
|
|
logger.info('Session expired due to inactivity');
|
|
await logout(request);
|
|
return null;
|
|
}
|
|
|
|
// Update last activity time
|
|
userSession.lastActivity = now;
|
|
session.set('user', userSession);
|
|
|
|
return userSession;
|
|
}
|
|
|
|
// Require authentication
|
|
export async function requireAuth(
|
|
request: Request,
|
|
redirectTo: string = new URL(request.url).pathname
|
|
) {
|
|
const user = await getUserFromSession(request);
|
|
|
|
if (!user) {
|
|
const searchParams = new URLSearchParams([['redirectTo', redirectTo]]);
|
|
throw redirect(`/login?${searchParams}`);
|
|
}
|
|
|
|
return user;
|
|
}
|
|
|
|
// Logout
|
|
export async function logout(request: Request) {
|
|
const session = await getSession(request);
|
|
|
|
return redirect('/', {
|
|
headers: {
|
|
'Set-Cookie': await sessionStorage.destroySession(session),
|
|
},
|
|
});
|
|
}
|
|
|
|
// Generate a secure random token
|
|
export function generateSecureToken(length = 32): string {
|
|
const array = new Uint8Array(length);
|
|
crypto.getRandomValues(array);
|
|
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
} |