import type { FastifyInstance, FastifyReply } from 'fastify' import { randomBytes } from 'node:crypto' import { createRefreshSession, touchLastLogin, type UserRow, } from '@authportal/db' import { issueAccessToken } from './issue-access-token.js' import { safeAudit } from './audit.js' import { targetAppFromReturnTo } from './target-app.js' export const REFRESH_COOKIE = 'refresh_token' export function completeLogin( app: FastifyInstance, reply: FastifyReply, user: UserRow, opts: { method: 'password' | 'passkey' returnTo?: string ip: string | null userAgent: string | null }, ) { const body = issueAccessToken(app, user) const refreshRaw = randomBytes(32).toString('hex') const refreshExpires = new Date( Date.now() + app.config.refreshTtlDays * 24 * 60 * 60 * 1000, ) createRefreshSession(app.db, user.id, refreshRaw, refreshExpires, { ip: opts.ip, userAgent: opts.userAgent, }) touchLastLogin(app.db, user.id, opts.ip) reply.header( 'Set-Cookie', `${REFRESH_COOKIE}=${refreshRaw}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${app.config.refreshTtlDays * 86400}${app.config.isProd ? '; Secure' : ''}`, ) const targetApp = targetAppFromReturnTo(opts.returnTo) const isPasskey = opts.method === 'passkey' safeAudit(app, { action: isPasskey ? 'auth.passkey_login' : 'auth.login', severity: 'info', actorUserId: user.id, actorEmail: user.email, actorName: user.name, targetType: 'session', targetId: user.id, summary: isPasskey ? `Вход (passkey): ${user.email}` : `Вход: ${user.email}`, details: { method: opts.method, user_agent: opts.userAgent, return_to: opts.returnTo ?? null, target_app: targetApp, }, ip: opts.ip, }) return body }