Login sequence and inventory/protocol storage groundwork

This commit is contained in:
2026-03-19 05:42:11 +00:00
parent 5b2c7e4506
commit 55bbd6909d
21 changed files with 3882 additions and 157 deletions

37
server/src/auth/auth.ts Normal file
View File

@@ -0,0 +1,37 @@
import { betterAuth } from 'better-auth';
import { kyselyAdapter } from '@better-auth/kysely-adapter';
import { Kysely, PostgresDialect } from 'kysely';
import { Pool } from 'pg';
const db = new Kysely({
dialect: new PostgresDialect({
pool: new Pool({
connectionString: process.env.DATABASE_URL ||
'postgresql://labwise:labwise_dev_pw@localhost:5432/labwise_db',
}),
}),
});
export const auth = betterAuth({
database: kyselyAdapter(db, { type: 'postgres' }),
baseURL: process.env.BETTER_AUTH_URL || 'http://localhost:3001',
secret: process.env.BETTER_AUTH_SECRET || 'dev-secret-change-in-production-min32chars!!',
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID || '',
clientSecret: process.env.GOOGLE_CLIENT_SECRET || '',
},
microsoft: {
clientId: process.env.MICROSOFT_CLIENT_ID || '',
clientSecret: process.env.MICROSOFT_CLIENT_SECRET || '',
tenantId: process.env.MICROSOFT_TENANT_ID || 'common',
},
},
trustedOrigins: [
'http://localhost:5173',
'https://labwise.wahwa.com',
],
});

View File

@@ -0,0 +1,30 @@
import { Request, Response, NextFunction } from 'express';
import { auth } from './auth';
import { fromNodeHeaders } from 'better-auth/node';
declare global {
namespace Express {
interface Request {
user?: { id: string; email: string; name: string };
sessionId?: string;
}
}
}
export async function requireAuth(req: Request, res: Response, next: NextFunction) {
try {
const session = await auth.api.getSession({
headers: fromNodeHeaders(req.headers),
});
if (!session) {
return res.status(401).json({ error: 'Unauthorized' });
}
req.user = session.user;
req.sessionId = session.session.id;
next();
} catch {
return res.status(401).json({ error: 'Unauthorized' });
}
}

View File

@@ -0,0 +1,16 @@
import rateLimit from 'express-rate-limit';
export const authRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 20,
standardHeaders: true,
legacyHeaders: false,
message: { error: 'Too many auth attempts, please try again later' },
});
export const apiRateLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 200,
standardHeaders: true,
legacyHeaders: false,
});