2025-10-25 11:39:24 +02:00

68 lines
1.9 KiB
TypeScript

import { jwtVerify } from "jose";
// Secret key for JWT verification (must match the one used for signing)
const rawSecret = (process.env.JWT_SECRET ?? "").trim().replace(/^"|"$/g, "");
const JWT_SECRET = new TextEncoder().encode(rawSecret);
export interface JWTPayload {
email: string;
role: string;
iat: number;
exp: number;
}
/**
* Validates a JWT token and returns the payload if valid
*/
export async function validateToken(token: string): Promise<JWTPayload | null> {
try {
const raw = token.startsWith("Bearer ") ? token.slice(7) : token;
const { payload } = await jwtVerify(raw, JWT_SECRET, {
algorithms: ["HS256"],
});
return payload as unknown as JWTPayload;
} catch (error) {
console.error("Token validation error:", error);
return null;
}
}
/**
* Checks if a token is expired
*/
export function isTokenExpired(payload: JWTPayload): boolean {
const currentTime = Math.floor(Date.now() / 1000);
return payload.exp < currentTime;
}
/**
* Gets token expiration time in a human-readable format
*/
export function getTokenExpirationTime(payload: JWTPayload): Date {
return new Date(payload.exp * 1000);
}
/**
* Gets time until token expires in seconds
*/
export function getTimeUntilExpiration(payload: JWTPayload): number {
const currentTime = Math.floor(Date.now() / 1000);
return Math.max(0, payload.exp - currentTime);
}
export function validatePassword(password: string) {
const errors: string[] = [];
if (password.length < 8) errors.push("At least 8 characters");
if (!/[A-Z]/.test(password)) errors.push("One uppercase letter");
if (!/[a-z]/.test(password)) errors.push("One lowercase letter");
if (!/[0-9]/.test(password)) errors.push("One number");
if (!/[!@#$%^&*(),.?":{}|<>]/.test(password))
errors.push("One special character");
return {
valid: errors.length === 0,
errors,
};
}