socialconnect / src /lib /auth.ts
getalvi's picture
Upload 51 files
a92567e verified
Raw
History Blame Contribute Delete
3.56 kB
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;
}