accounts/dashboard/app/api/auth/mfa/setup/route.ts
Haitao Pan 5f82e984b4 fix: update cookies() API for Next.js 16 compatibility
- Updated 6 API route handlers to use await cookies()
- Fixed: auth/session, auth/login, auth/mfa/* endpoints
- Resolves "cookies().get is not a function" errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-06 10:35:56 +08:00

100 lines
3.1 KiB
TypeScript

import { cookies } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server'
import { applyMfaCookie, MFA_COOKIE_NAME, SESSION_COOKIE_NAME } from '@lib/authGateway'
import { getAccountServiceApiBaseUrl } from '@server/serviceConfig'
const ACCOUNT_API_BASE = getAccountServiceApiBaseUrl()
// This Next.js route proxies MFA provisioning requests to the account service.
// The UI calls /api/auth/mfa/setup, which in turn forwards to the Go backend
// at /api/auth/mfa/totp/provision, keeping browser credentials opaque to the
// external service and letting us manage cookies centrally.
type SetupPayload = {
token?: string
issuer?: string
account?: string
}
function normalizeString(value: unknown) {
return typeof value === 'string' ? value.trim() : ''
}
export async function POST(request: NextRequest) {
const cookieStore = await cookies()
let payload: SetupPayload
try {
payload = (await request.json()) as SetupPayload
} catch (error) {
console.error('Failed to decode MFA setup payload', error)
return NextResponse.json({ success: false, error: 'invalid_request', needMfa: true }, { status: 400 })
}
const sessionToken = cookieStore.get(SESSION_COOKIE_NAME)?.value ?? ''
const cookieToken = cookieStore.get(MFA_COOKIE_NAME)?.value ?? ''
const token = normalizeString(payload?.token || cookieToken)
if (!token && !sessionToken) {
return NextResponse.json({ success: false, error: 'mfa_token_required', needMfa: true }, { status: 400 })
}
const issuer = normalizeString(payload?.issuer)
const account = normalizeString(payload?.account)
try {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
}
if (sessionToken) {
headers.Authorization = `Bearer ${sessionToken}`
}
const body: Record<string, string> = {}
if (token) {
body.token = token
}
if (issuer) {
body.issuer = issuer
}
if (account) {
body.account = account
}
const response = await fetch(`${ACCOUNT_API_BASE}/mfa/totp/provision`, {
method: 'POST',
headers,
body: JSON.stringify(body),
cache: 'no-store',
})
const data = await response.json().catch(() => ({}))
if (!response.ok) {
const errorCode = typeof (data as { error?: string })?.error === 'string' ? data.error : 'mfa_setup_failed'
return NextResponse.json({ success: false, error: errorCode, needMfa: true }, { status: response.status || 400 })
}
const result = NextResponse.json({ success: true, error: null, needMfa: true, data })
const nextToken = normalizeString((data as { mfaToken?: string })?.mfaToken || token || cookieToken)
if (nextToken) {
applyMfaCookie(result, nextToken)
}
return result
} catch (error) {
console.error('Account service MFA setup proxy failed', error)
return NextResponse.json({ success: false, error: 'account_service_unreachable', needMfa: true }, { status: 502 })
}
}
export function GET() {
return NextResponse.json(
{ success: false, error: 'method_not_allowed', needMfa: true },
{
status: 405,
headers: {
Allow: 'POST',
},
},
)
}