Spaces:
Configuration error
Configuration error
| import { NextRequest } from 'next/server'; | |
| import { db } from '@/lib/db'; | |
| import { createHmac, randomBytes } from 'crypto'; | |
| // ============================================================ | |
| // JWT-like Token Management (Simple HMAC-based) | |
| // ============================================================ | |
| const JWT_SECRET = process.env.JWT_SECRET || 'socialconnect-secret-key-2024'; | |
| const TOKEN_EXPIRY = 24 * 60 * 60 * 1000; // 24 hours | |
| const REFRESH_TOKEN_EXPIRY = 7 * 24 * 60 * 60 * 1000; // 7 days | |
| interface TokenPayload { | |
| userId: string; | |
| email: string; | |
| role: string; | |
| exp: number; | |
| type: 'access' | 'refresh'; | |
| } | |
| function base64UrlEncode(data: string): string { | |
| return Buffer.from(data).toString('base64url'); | |
| } | |
| function base64UrlDecode(data: string): string { | |
| return Buffer.from(data, 'base64url').toString(); | |
| } | |
| export function generateToken(payload: Omit<TokenPayload, 'exp' | 'type'>, type: 'access' | 'refresh' = 'access'): string { | |
| const exp = Date.now() + (type === 'refresh' ? REFRESH_TOKEN_EXPIRY : TOKEN_EXPIRY); | |
| const fullPayload: TokenPayload = { ...payload, exp, type }; | |
| const header = base64UrlEncode(JSON.stringify({ alg: 'HS256', typ: 'JWT' })); | |
| const payloadStr = base64UrlEncode(JSON.stringify(fullPayload)); | |
| const signature = createHmac('sha256', JWT_SECRET).update(`${header}.${payloadStr}`).digest('base64url'); | |
| return `${header}.${payloadStr}.${signature}`; | |
| } | |
| export function verifyToken(token: string): TokenPayload | null { | |
| try { | |
| const parts = token.split('.'); | |
| if (parts.length !== 3) return null; | |
| const [header, payload, signature] = parts; | |
| const expectedSignature = createHmac('sha256', JWT_SECRET).update(`${header}.${payload}`).digest('base64url'); | |
| if (signature !== expectedSignature) return null; | |
| const decoded = JSON.parse(base64UrlDecode(payload)) as TokenPayload; | |
| if (decoded.exp < Date.now()) return null; | |
| return decoded; | |
| } catch { | |
| return null; | |
| } | |
| } | |
| // ============================================================ | |
| // Password Hashing | |
| // ============================================================ | |
| export function hashPassword(password: string): string { | |
| const salt = randomBytes(16).toString('hex'); | |
| const hash = createHmac('sha256', salt).update(password).digest('hex'); | |
| return `${salt}:${hash}`; | |
| } | |
| export function verifyPassword(password: string, stored: string): boolean { | |
| const [salt, hash] = stored.split(':'); | |
| const verifyHash = createHmac('sha256', salt).update(password).digest('hex'); | |
| return hash === verifyHash; | |
| } | |
| // ============================================================ | |
| // Auth Middleware Helper | |
| // ============================================================ | |
| export interface AuthUser { | |
| userId: string; | |
| email: string; | |
| role: string; | |
| } | |
| export async function getAuthUser(request: NextRequest): Promise<AuthUser | null> { | |
| const authHeader = request.headers.get('authorization'); | |
| if (!authHeader?.startsWith('Bearer ')) return null; | |
| const token = authHeader.substring(7); | |
| const payload = verifyToken(token); | |
| if (!payload) return null; | |
| // Verify user still exists and is active | |
| const user = await db.user.findUnique({ where: { id: payload.userId } }); | |
| if (!user || !user.isActive) return null; | |
| return { userId: user.id, email: user.email, role: user.role }; | |
| } | |
| export function requireRole(user: AuthUser | null, roles: string[]): AuthUser { | |
| if (!user) throw new Error('Authentication required'); | |
| if (!roles.includes(user.role)) throw new Error('Insufficient permissions'); | |
| return user; | |
| } | |