171 lines
4.5 KiB
JavaScript
171 lines
4.5 KiB
JavaScript
import express from 'express';
|
|
import { AppError } from '../lib/errors.js';
|
|
import {
|
|
getSessionForActor,
|
|
parseClientSignIn,
|
|
parseClientSignUp,
|
|
parseStaffPhoneStart,
|
|
parseStaffPhoneVerify,
|
|
signInClient,
|
|
signOutActor,
|
|
signUpClient,
|
|
startStaffPhoneAuth,
|
|
verifyStaffPhoneAuth,
|
|
} from '../services/auth-service.js';
|
|
import { verifyFirebaseToken } from '../services/firebase-auth.js';
|
|
|
|
const defaultAuthService = {
|
|
parseClientSignIn,
|
|
parseClientSignUp,
|
|
parseStaffPhoneStart,
|
|
parseStaffPhoneVerify,
|
|
signInClient,
|
|
signOutActor,
|
|
signUpClient,
|
|
startStaffPhoneAuth,
|
|
verifyStaffPhoneAuth,
|
|
getSessionForActor,
|
|
};
|
|
|
|
function getBearerToken(header) {
|
|
if (!header) return null;
|
|
const [scheme, token] = header.split(' ');
|
|
if (!scheme || scheme.toLowerCase() !== 'bearer' || !token) return null;
|
|
return token;
|
|
}
|
|
|
|
async function requireAuth(req, _res, next) {
|
|
try {
|
|
const token = getBearerToken(req.get('Authorization'));
|
|
if (!token) {
|
|
throw new AppError('UNAUTHENTICATED', 'Missing bearer token', 401);
|
|
}
|
|
|
|
if (process.env.AUTH_BYPASS === 'true') {
|
|
req.actor = { uid: 'test-user', email: 'test@krow.local', role: 'TEST' };
|
|
return next();
|
|
}
|
|
|
|
const decoded = await verifyFirebaseToken(token);
|
|
req.actor = {
|
|
uid: decoded.uid,
|
|
email: decoded.email || null,
|
|
role: decoded.role || null,
|
|
};
|
|
return next();
|
|
} catch (error) {
|
|
if (error instanceof AppError) return next(error);
|
|
return next(new AppError('UNAUTHENTICATED', 'Token verification failed', 401));
|
|
}
|
|
}
|
|
|
|
export function createAuthRouter(options = {}) {
|
|
const router = express.Router();
|
|
const fetchImpl = options.fetchImpl || fetch;
|
|
const authService = options.authService || defaultAuthService;
|
|
|
|
router.use(express.json({ limit: '1mb' }));
|
|
|
|
router.post('/client/sign-in', async (req, res, next) => {
|
|
try {
|
|
const payload = authService.parseClientSignIn(req.body);
|
|
const session = await authService.signInClient(payload, { fetchImpl });
|
|
return res.status(200).json({
|
|
...session,
|
|
requestId: req.requestId,
|
|
});
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
});
|
|
|
|
router.post('/client/sign-up', async (req, res, next) => {
|
|
try {
|
|
const payload = authService.parseClientSignUp(req.body);
|
|
const session = await authService.signUpClient(payload, { fetchImpl });
|
|
return res.status(201).json({
|
|
...session,
|
|
requestId: req.requestId,
|
|
});
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
});
|
|
|
|
router.post('/staff/phone/start', async (req, res, next) => {
|
|
try {
|
|
const payload = authService.parseStaffPhoneStart(req.body);
|
|
const result = await authService.startStaffPhoneAuth(payload, { fetchImpl });
|
|
return res.status(200).json({
|
|
...result,
|
|
requestId: req.requestId,
|
|
});
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
});
|
|
|
|
router.post('/staff/phone/verify', async (req, res, next) => {
|
|
try {
|
|
const payload = authService.parseStaffPhoneVerify(req.body);
|
|
const session = await authService.verifyStaffPhoneAuth(payload, { fetchImpl });
|
|
return res.status(200).json({
|
|
...session,
|
|
requestId: req.requestId,
|
|
});
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
});
|
|
|
|
router.get('/session', requireAuth, async (req, res, next) => {
|
|
try {
|
|
const session = await authService.getSessionForActor(req.actor);
|
|
return res.status(200).json({
|
|
...session,
|
|
requestId: req.requestId,
|
|
});
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
});
|
|
|
|
router.post('/sign-out', requireAuth, async (req, res, next) => {
|
|
try {
|
|
const result = await authService.signOutActor(req.actor);
|
|
return res.status(200).json({
|
|
...result,
|
|
requestId: req.requestId,
|
|
});
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
});
|
|
|
|
router.post('/client/sign-out', requireAuth, async (req, res, next) => {
|
|
try {
|
|
const result = await authService.signOutActor(req.actor);
|
|
return res.status(200).json({
|
|
...result,
|
|
requestId: req.requestId,
|
|
});
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
});
|
|
|
|
router.post('/staff/sign-out', requireAuth, async (req, res, next) => {
|
|
try {
|
|
const result = await authService.signOutActor(req.actor);
|
|
return res.status(200).json({
|
|
...result,
|
|
requestId: req.requestId,
|
|
});
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
});
|
|
|
|
return router;
|
|
}
|