← back to Professional Directory
agents/api-agent/routes/auth.js
62 lines
/**
* /auth/* routes — Google OAuth login, logout, /whoami.
*
* Mounted in agents/api-agent/server.js after session middleware.
*/
const express = require('express');
const { passport, authConfigured } = require('../auth');
const router = express.Router();
// Google OAuth start.
router.get('/google', (req, res, next) => {
if (!authConfigured()) return res.status(503).json({ error: 'Google OAuth not configured (set GOOGLE_OAUTH_CLIENT_ID / SECRET)' });
// Passport reads `returnTo` and bounces back here after callback.
// SECURITY: `/foo` is OK; `//evil.com/x` is NOT — that's an open-redirect
// (browser treats it as protocol-relative, sends user off-site).
const returnTo = String(req.query.returnTo || '/');
req.session.returnTo = /^\/(?!\/)/.test(returnTo) ? returnTo : '/';
passport.authenticate('google', { scope: ['email', 'profile'] })(req, res, next);
});
// Google OAuth callback — passport completes session login here.
router.get('/google/callback',
(req, res, next) => {
if (!authConfigured()) return res.status(503).json({ error: 'Google OAuth not configured' });
next();
},
passport.authenticate('google', { failureRedirect: '/auth/google?failed=1' }),
(req, res) => {
const target = req.session.returnTo || '/';
delete req.session.returnTo;
res.redirect(target);
});
router.post('/logout', (req, res, next) => {
req.logout(err => {
if (err) return next(err);
req.session.destroy(() => res.json({ ok: true }));
});
});
// JSON identity endpoint — returns session user or null.
router.get('/whoami', (req, res) => {
if (!req.user) return res.json({ user: null });
const u = req.user;
res.json({
user: {
id: u.id,
email: u.email,
role: u.role,
tier: u.tier,
display_name: u.display_name,
avatar_url: u.avatar_url,
claimed_professional_id: u.claimed_professional_id,
claimed_organization_id: u.claimed_organization_id,
synthetic: !!u._synthetic,
},
});
});
module.exports = router;