Files
inventory/inventory-server/auth/server.js
T
2026-05-23 19:38:12 -04:00

85 lines
2.7 KiB
JavaScript

import 'dotenv/config';
import express from 'express';
import cors from 'cors';
import pg from 'pg';
import { fileURLToPath } from 'node:url';
const { Pool } = pg;
import { dirname, resolve as resolvePath } from 'node:path';
import { config as loadEnv } from 'dotenv';
import { corsOptions } from '../shared/cors/policy.js';
import { requestLog } from '../shared/logging/request-log.js';
import { logger } from '../shared/logging/logger.js';
import { errorHandler } from '../shared/errors/handler.js';
import { loginLimiter, verifyLimiter } from '../shared/rate-limit/login.js';
import { extractBearerToken, verifyToken, TokenError } from '../shared/auth/verify.js';
import { createAuthRoutes } from './routes.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// auth/ lives at inventory-server/auth/, so .env one level up
loadEnv({ path: resolvePath(__dirname, '../.env') });
if (!process.env.JWT_SECRET) {
logger.error('JWT_SECRET is not set; refusing to start');
process.exit(1);
}
logger.info({
host: process.env.DB_HOST,
user: process.env.DB_USER,
database: process.env.DB_NAME,
port: process.env.DB_PORT,
auth_port: process.env.AUTH_PORT,
}, 'starting auth server');
const app = express();
const port = Number(process.env.AUTH_PORT) || 3011;
const pool = new Pool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
port: Number(process.env.DB_PORT) || 5432,
});
app.use(requestLog());
app.use(express.json({ limit: '1mb' }));
app.use(cors(corsOptions));
// Caddy forward_auth target: JWT signature check only, no DB hit.
// Returns 200 with X-User-Id / X-User-Username on success; 401 otherwise.
// Per-service middleware re-verifies independently; these headers are informational.
app.all('/verify', verifyLimiter, (req, res) => {
try {
const token = extractBearerToken(req.headers.authorization);
const decoded = verifyToken(token, process.env.JWT_SECRET);
res.set('X-User-Id', String(decoded.userId));
if (decoded.username) res.set('X-User-Username', decoded.username);
res.status(200).end();
} catch (err) {
if (err instanceof TokenError) {
return res.status(401).json({ error: err.message });
}
res.status(401).json({ error: 'Invalid token' });
}
});
// Login route gets its own rate limiter to slow credential stuffing.
app.use('/login', loginLimiter);
// Mount user-management + /login + /me from routes.js
app.use('/', createAuthRoutes({ pool }));
app.get('/health', (req, res) => res.json({ status: 'healthy' }));
app.use(errorHandler);
app.listen(port, () => {
logger.info({ port }, 'auth server listening');
});