mirror of
https://github.com/p-stream/backend.git
synced 2026-01-11 20:10:33 +00:00
134 lines
3.5 KiB
TypeScript
134 lines
3.5 KiB
TypeScript
import { prisma } from './prisma';
|
|
import jwt from 'jsonwebtoken';
|
|
const { sign, verify } = jwt;
|
|
import { randomUUID } from 'crypto';
|
|
|
|
// 21 days in ms
|
|
const SESSION_EXPIRY_MS = 21 * 24 * 60 * 60 * 1000;
|
|
|
|
export function useAuth() {
|
|
const getSession = async (id: string) => {
|
|
const session = await prisma.sessions.findUnique({
|
|
where: { id },
|
|
});
|
|
|
|
if (!session) return null;
|
|
if (new Date(session.expires_at) < new Date()) return null;
|
|
|
|
return session;
|
|
};
|
|
|
|
const getSessionAndBump = async (id: string) => {
|
|
const session = await getSession(id);
|
|
if (!session) return null;
|
|
|
|
const now = new Date();
|
|
const expiryDate = new Date(now.getTime() + SESSION_EXPIRY_MS);
|
|
|
|
return await prisma.sessions.update({
|
|
where: { id },
|
|
data: {
|
|
accessed_at: now,
|
|
expires_at: expiryDate,
|
|
},
|
|
});
|
|
};
|
|
|
|
const makeSession = async (user: string, device: string, userAgent?: string) => {
|
|
if (!userAgent) throw new Error('No useragent provided');
|
|
|
|
const now = new Date();
|
|
const expiryDate = new Date(now.getTime() + SESSION_EXPIRY_MS);
|
|
|
|
return await prisma.sessions.create({
|
|
data: {
|
|
id: randomUUID(),
|
|
user,
|
|
device,
|
|
user_agent: userAgent,
|
|
created_at: now,
|
|
accessed_at: now,
|
|
expires_at: expiryDate,
|
|
},
|
|
});
|
|
};
|
|
|
|
const makeSessionToken = (session: { id: string }) => {
|
|
const runtimeConfig = useRuntimeConfig();
|
|
const cryptoSecret = runtimeConfig.cryptoSecret || process.env.CRYPTO_SECRET;
|
|
|
|
if (!cryptoSecret) {
|
|
console.error('CRYPTO_SECRET is missing from both runtime config and environment');
|
|
console.error('Available runtime config keys:', Object.keys(runtimeConfig));
|
|
console.error('Environment variables:', {
|
|
CRYPTO_SECRET: process.env.CRYPTO_SECRET ? 'SET' : 'NOT SET',
|
|
NODE_ENV: process.env.NODE_ENV,
|
|
});
|
|
throw new Error('CRYPTO_SECRET environment variable is not set');
|
|
}
|
|
|
|
return sign({ sid: session.id }, cryptoSecret, {
|
|
algorithm: 'HS256',
|
|
});
|
|
};
|
|
|
|
const verifySessionToken = (token: string) => {
|
|
try {
|
|
const runtimeConfig = useRuntimeConfig();
|
|
const cryptoSecret = runtimeConfig.cryptoSecret || process.env.CRYPTO_SECRET;
|
|
|
|
if (!cryptoSecret) {
|
|
console.error('CRYPTO_SECRET is missing for token verification');
|
|
return null;
|
|
}
|
|
|
|
const payload = verify(token, cryptoSecret, {
|
|
algorithms: ['HS256'],
|
|
});
|
|
|
|
if (typeof payload === 'string') return null;
|
|
return payload as { sid: string };
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const getCurrentSession = async () => {
|
|
const event = useEvent();
|
|
const authHeader = getRequestHeader(event, 'authorization');
|
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Unauthorized',
|
|
});
|
|
}
|
|
|
|
const token = authHeader.split(' ')[1];
|
|
const payload = verifySessionToken(token);
|
|
if (!payload) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Invalid token',
|
|
});
|
|
}
|
|
|
|
const session = await getSessionAndBump(payload.sid);
|
|
if (!session) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Session not found or expired',
|
|
});
|
|
}
|
|
|
|
return session;
|
|
};
|
|
|
|
return {
|
|
getSession,
|
|
getSessionAndBump,
|
|
makeSession,
|
|
makeSessionToken,
|
|
verifySessionToken,
|
|
getCurrentSession,
|
|
};
|
|
}
|