core-edge / src /lib /auth.ts
dragxd's picture
Initial commit: VoltEdge project
3353b25
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';
import { NextRequest } from 'next/server';
import { getUserById, getApiKeyByHash, updateApiKeyLastUsed } from './db';
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
const JWT_EXPIRES_IN = '7d';
export interface JWTPayload {
userId: string;
email: string;
type: 'user' | 'apikey';
}
// Password hashing
export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, 10);
}
export async function verifyPassword(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
// JWT Token generation
export function generateToken(payload: JWTPayload): string {
return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
}
export function verifyToken(token: string): JWTPayload | null {
try {
return jwt.verify(token, JWT_SECRET) as JWTPayload;
} catch {
return null;
}
}
// API Key generation
export function generateApiKey(): string {
const prefix = 'px_';
const randomPart = Array.from(crypto.getRandomValues(new Uint8Array(32)))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
return `${prefix}${randomPart}`;
}
export async function hashApiKey(key: string): Promise<string> {
// Use a simple hash for API keys (in production, use proper hashing)
const encoder = new TextEncoder();
const data = encoder.encode(key);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
// Authentication middleware
export async function authenticateRequest(req: NextRequest): Promise<{ userId: string; email?: string; apiKey?: any } | null> {
// Try JWT token first (from Authorization header or cookie)
const authHeader = req.headers.get('authorization');
let token: string | null = null;
if (authHeader?.startsWith('Bearer ')) {
token = authHeader.substring(7);
} else {
// Try cookie
token = req.cookies.get('auth_token')?.value || null;
}
if (token) {
const payload = verifyToken(token);
if (payload && payload.type === 'user') {
const user = await getUserById(payload.userId);
if (user) {
return { userId: user.id, email: user.email };
}
}
}
// Try API key (X-API-Key header)
const apiKey = req.headers.get('x-api-key');
if (apiKey) {
const keyHash = await hashApiKey(apiKey);
const apiKeyRecord = await getApiKeyByHash(keyHash);
if (apiKeyRecord && apiKeyRecord.is_active) {
await updateApiKeyLastUsed(apiKeyRecord.id);
return { userId: apiKeyRecord.user_id, apiKey: apiKeyRecord };
}
}
return null;
}
// Require authentication
export async function requireAuth(req: NextRequest): Promise<{ userId: string; email?: string; apiKey?: any }> {
const auth = await authenticateRequest(req);
if (!auth) {
throw new Error('UNAUTHORIZED');
}
return auth;
}