import { authenticate } from '../../plugins/auth.js' import { getCcUserById } from '../../services/cc-user.service.js' import { signInCcUser, refreshTokens, logout, } from '../../services/auth.service.js' import { UserType } from '../../constants.js' const REFRESH_COOKIE_NAME = 'cc_refresh_token' const extractDeviceInfo = (request) => ({ user_agent: request.headers['user-agent'] || null, ip: request.ip || null, }) const sendAuthError = (reply, err) => { if (!err.statusCode) reply.request.log.error({ err }, 'Unhandled auth error') return reply.code(err.statusCode || 500).send({ success: false, error: { code: err.code || 'INTERNAL', message: err.message, ...(err.details && { details: err.details }), }, }) } const cookieOpts = () => ({ httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', path: '/', }) const setRefreshCookie = (reply, refreshToken, expiresAt) => { reply.setCookie(REFRESH_COOKIE_NAME, refreshToken, { ...cookieOpts(), expires: new Date(expiresAt), }) } const clearRefreshCookie = (reply) => { reply.clearCookie(REFRESH_COOKIE_NAME, cookieOpts()) } export const internalAuthRoutes = async (app) => { app.post('/login', async (request, reply) => { const { email, password } = request.body || {} if (!email || !password) { return reply.code(422).send({ success: false, error: { code: 'VALIDATION_ERROR', message: 'email and password are required' }, }) } try { const { tokens, profile } = await signInCcUser({ email, password, deviceInfo: extractDeviceInfo(request), }) setRefreshCookie(reply, tokens.refresh_token, tokens.refresh_token_expires_at) return reply.send({ success: true, data: { access_token: tokens.access_token, access_token_expires_in: tokens.access_token_expires_in, profile, }, }) } catch (err) { return sendAuthError(reply, err) } }) app.post('/refresh', async (request, reply) => { const refreshToken = request.cookies?.[REFRESH_COOKIE_NAME] if (!refreshToken) { return reply.code(401).send({ success: false, error: { code: 'REFRESH_MISSING', message: 'Refresh token missing' }, }) } try { const { tokens, profile } = await refreshTokens({ refreshToken, deviceInfo: extractDeviceInfo(request), }) setRefreshCookie(reply, tokens.refresh_token, tokens.refresh_token_expires_at) return reply.send({ success: true, data: { access_token: tokens.access_token, access_token_expires_in: tokens.access_token_expires_in, profile, }, }) } catch (err) { clearRefreshCookie(reply) return sendAuthError(reply, err) } }) app.post('/logout', { preHandler: authenticate }, async (request, reply) => { await logout({ sessionId: request.auth.sessionId }) clearRefreshCookie(reply) return reply.send({ success: true }) }) app.get('/me', { preHandler: authenticate }, async (request, reply) => { if (request.auth.userType !== UserType.CC_USER) { return reply.code(403).send({ success: false, error: { code: 'FORBIDDEN', message: 'Control center account required' }, }) } const user = await getCcUserById(request.auth.userId) if (!user) { return reply.code(404).send({ success: false, error: { code: 'NOT_FOUND', message: 'Control center account not found' }, }) } return reply.send({ success: true, data: { id: user.id, email: user.email, display_name: user.display_name, role: user.role, }, }) }) }