const express = require('express') const { body } = require('express-validator') const { login, loginTotp, logout, me, HONEYPOT_FIELD } = require('./auth.controller') const { isLoggedIn } = require('../../../utils/auth') const { loginLimiter } = require('../../../middleware/rateLimit') const { slowLogin, backoffGuard } = require('../../../middleware/loginProtection') const validate = require('../../../middleware/validate') const mobileRouter = require('./mobile.routes') const ssoRouter = require('./sso.routes') const authRouter = express.Router() // Native/Android bearer-token auth. Additive alongside the web cookie flow below. authRouter.use('/mobile', mobileRouter) // SSO discovery + OAuth redirect flow (/auth/providers, /auth/sso/:provider/*). // Additive; the web cookie + TOTP flow below is unchanged. authRouter.use(ssoRouter) // Login protection order (cheapest rejection first): // backoffGuard → per-IP exponential lockout on repeated failures // slowLogin → progressive per-request delay within the window // loginLimiter → hard 10-per-15-min cap const loginGuards = [backoffGuard, slowLogin, loginLimiter] authRouter.post( '/login', // #swagger.tags = ['Auth'] // #swagger.summary = 'Log in with username and password' // #swagger.description = 'On success sets the httpOnly session cookie. If the account has 2FA enabled, returns { totpRequired, challenge } instead and no cookie is set — complete login at POST /login/totp. Rate limited and behind bot/backoff guards.' /* #swagger.requestBody = { required: true, content: { "application/json": { schema: { $ref: "#/components/schemas/LoginRequest" } } } } */ /* #swagger.responses[200] = { description: 'Session issued, or TOTP challenge required', content: { "application/json": { schema: { $ref: "#/components/schemas/LoginResponse" } } } } */ /* #swagger.responses[400] = { description: 'Validation error', content: { "application/json": { schema: { $ref: "#/components/schemas/ValidationError" } } } } */ /* #swagger.responses[401] = { description: 'Incorrect username or password', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */ /* #swagger.responses[429] = { description: 'Too many attempts (rate limited / backoff)', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */ ...loginGuards, body('username').isString().trim().notEmpty(), body('password').isString().notEmpty(), // Honeypot must be absent/empty for humans; bots that fill it are caught in // the controller. Accept-but-ignore here so a filled value still reaches it. body(HONEYPOT_FIELD).optional(), validate, login, ) // Second factor: same throttling, since it's a code-guessing surface too. authRouter.post( '/login/totp', // #swagger.tags = ['Auth'] // #swagger.summary = 'Complete login with a TOTP code' // #swagger.description = 'Second step for 2FA accounts. Exchange the challenge from /login plus the current authenticator code for a session cookie.' /* #swagger.requestBody = { required: true, content: { "application/json": { schema: { $ref: "#/components/schemas/TotpLoginRequest" } } } } */ /* #swagger.responses[200] = { description: 'Session issued', content: { "application/json": { schema: { $ref: "#/components/schemas/LoginResponse" } } } } */ /* #swagger.responses[400] = { description: 'Validation error', content: { "application/json": { schema: { $ref: "#/components/schemas/ValidationError" } } } } */ /* #swagger.responses[401] = { description: 'Invalid code or expired challenge', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */ /* #swagger.responses[429] = { description: 'Too many attempts (rate limited / backoff)', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */ ...loginGuards, body('challenge').isString().notEmpty(), body('code').isString().trim().isLength({ min: 6, max: 8 }), validate, loginTotp, ) authRouter.post( '/logout', // #swagger.tags = ['Auth'] // #swagger.summary = 'Log out (clear the session cookie)' /* #swagger.responses[200] = { description: 'Logged out', content: { "application/json": { schema: { $ref: "#/components/schemas/Message" } } } } */ logout, ) authRouter.get( '/me', // #swagger.tags = ['Auth'] // #swagger.summary = 'Current authenticated user' // #swagger.security = [{ "cookieAuth": [] }, { "bearerAuth": [] }] /* #swagger.responses[200] = { description: 'The signed-in user', content: { "application/json": { schema: { type: "object", properties: { user: { $ref: "#/components/schemas/User" } } } } } } */ /* #swagger.responses[401] = { description: 'Not authenticated', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */ isLoggedIn, me, ) module.exports = authRouter