eeec8f0a82
Build & Push / Pipeline Tests (push) Failing after 1m27s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Successful in 55s
Test / API Unit Tests (push) Successful in 1m9s
Test / Homepage Unit Tests (push) Successful in 50s
Test / Carplace Unit Tests (push) Has been cancelled
Test / Admin Unit Tests (push) Has been cancelled
Test / Dashboard Unit Tests (push) Has been cancelled
Test / API Integration Tests (push) Has been cancelled
58 lines
2.1 KiB
TypeScript
58 lines
2.1 KiB
TypeScript
import { Router } from 'express'
|
|
import { z } from 'zod'
|
|
import { parseBody } from '../../http/validate'
|
|
import { ok } from '../../http/respond'
|
|
import { AppError } from '../../http/errors'
|
|
import { setSessionCookie, clearSessionCookie } from '../../security/sessionCookies'
|
|
import * as employeeService from './auth.employee.service'
|
|
import * as adminService from '../admin/admin.service'
|
|
|
|
const unifiedLoginSchema = z.object({
|
|
email: z.string().email().max(255).trim().toLowerCase(),
|
|
password: z.string().max(128),
|
|
totpCode: z.string().length(6).optional(),
|
|
recoveryCode: z.string().min(8).max(32).optional(),
|
|
})
|
|
|
|
const router = Router()
|
|
|
|
router.post('/login', async (req, res, next) => {
|
|
try {
|
|
const { email, password, totpCode, recoveryCode } = parseBody(unifiedLoginSchema, req)
|
|
|
|
if (!totpCode && !recoveryCode) {
|
|
try {
|
|
const employeeResult = await employeeService.login({ email, password })
|
|
if (!('token' in employeeResult)) {
|
|
throw new AppError('Invalid email or password', 401, 'invalid_credentials')
|
|
}
|
|
clearSessionCookie(res, 'admin')
|
|
setSessionCookie(res, 'employee', employeeResult.token, 8 * 60 * 60 * 1000)
|
|
return ok(res, employeeResult)
|
|
} catch (err) {
|
|
if (!(err instanceof AppError) || err.error !== 'invalid_credentials') throw err
|
|
}
|
|
}
|
|
|
|
const adminResult = await adminService.login(email, password, totpCode, recoveryCode)
|
|
if (!adminResult) {
|
|
return res.status(401).json({ error: 'invalid_credentials', message: 'Invalid email or password', statusCode: 401 })
|
|
}
|
|
if ('totpRequired' in adminResult) {
|
|
clearSessionCookie(res, 'employee')
|
|
return res.status(401).json({ error: 'totp_required', message: '2FA code required', statusCode: 401 })
|
|
}
|
|
if ('invalidTotp' in adminResult) {
|
|
return res.status(401).json({ error: 'invalid_totp', message: 'Invalid 2FA code', statusCode: 401 })
|
|
}
|
|
|
|
clearSessionCookie(res, 'employee')
|
|
setSessionCookie(res, 'admin', adminResult.token, 8 * 60 * 60 * 1000)
|
|
return ok(res, adminResult)
|
|
} catch (err) {
|
|
return next(err)
|
|
}
|
|
})
|
|
|
|
export default router
|