50 lines
1.7 KiB
TypeScript
50 lines
1.7 KiB
TypeScript
import { Request, Response, NextFunction } from 'express'
|
|
import { prisma } from '../lib/prisma'
|
|
import { getAuthToken, sendUnauthorized } from './authHelpers'
|
|
import { verifyActorToken } from '../security/tokens'
|
|
import { getSessionCookieName } from '../security/sessionCookies'
|
|
import { actorLimiter } from './rateLimiter'
|
|
|
|
/**
|
|
* Validates a company-scoped Bearer token and loads the employee + company onto `req`.
|
|
*
|
|
* Guarantees on success:
|
|
* req.employee — full employee record (with company relation)
|
|
* req.company — the employee's company
|
|
* req.companyId — string shorthand for req.company.id
|
|
*/
|
|
async function authenticateCompanyRequest(req: Request, res: Response, next: NextFunction) {
|
|
const token = getAuthToken(req, getSessionCookieName('employee'))
|
|
|
|
if (!token) return sendUnauthorized(res, 'unauthenticated', 'Authentication required')
|
|
|
|
let payload: { sub: string; type: string }
|
|
try {
|
|
payload = verifyActorToken(token, 'employee')
|
|
} catch {
|
|
return sendUnauthorized(res, 'invalid_token', 'Invalid or expired session token')
|
|
}
|
|
|
|
const employee = await prisma.employee.findUnique({
|
|
where: { id: payload.sub },
|
|
include: { company: true },
|
|
})
|
|
|
|
if (!employee || !employee.isActive) {
|
|
return sendUnauthorized(res, 'unauthenticated', 'Employee account not found or inactive')
|
|
}
|
|
|
|
req.employee = employee
|
|
req.company = employee.company
|
|
req.companyId = employee.companyId
|
|
return actorLimiter(req, res, next)
|
|
}
|
|
|
|
export async function requireCompanyAuth(req: Request, res: Response, next: NextFunction) {
|
|
return authenticateCompanyRequest(req, res, next)
|
|
}
|
|
|
|
export async function requireCompanyDocumentAuth(req: Request, res: Response, next: NextFunction) {
|
|
return authenticateCompanyRequest(req, res, next)
|
|
}
|