77 lines
3.7 KiB
TypeScript
77 lines
3.7 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { cookies } from 'next/headers';
|
|
import { hasValidOrigin } from '../../../../lib/request-origin';
|
|
|
|
const actions = new Set(['login', 'register', 'refresh', 'logout', 'verify-email-request', 'verify-email', 'password-reset-request', 'password-reset', 'firebase-login']);
|
|
|
|
export async function POST(request: NextRequest, context: { params: Promise<{ action: string }> }) {
|
|
const { action } = await context.params;
|
|
if (!actions.has(action)) return NextResponse.json({ detail: 'Unknown authentication action.' }, { status: 404 });
|
|
if (action === 'logout') {
|
|
if (!hasValidOrigin(request)) {
|
|
return NextResponse.json({ detail: 'Invalid request origin.' }, { status: 403 });
|
|
}
|
|
const refreshToken = (await cookies()).get('motorsignal_refresh_token')?.value;
|
|
if (refreshToken) {
|
|
try {
|
|
const upstream = await fetch(`${process.env.MOTORSIGNAL_API_BASE_URL ?? 'https://api.motorsignal.app'}/api/v1/auth/logout`, {
|
|
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
body: JSON.stringify({ refresh_token: refreshToken }), cache: 'no-store',
|
|
});
|
|
if (!upstream.ok) return NextResponse.json({ detail: 'Unable to revoke session.' }, { status: 502 });
|
|
} catch {
|
|
return NextResponse.json({ detail: 'Unable to revoke session.' }, { status: 502 });
|
|
}
|
|
}
|
|
const response = new NextResponse(null, { status: 204 });
|
|
response.cookies.delete('motorsignal_access_token');
|
|
response.cookies.delete('motorsignal_refresh_token');
|
|
return response;
|
|
}
|
|
const baseUrl = process.env.MOTORSIGNAL_API_BASE_URL ?? 'https://api.motorsignal.app';
|
|
const endpoint = action === 'verify-email-request' ? 'verify-email/request'
|
|
: action === 'password-reset-request' ? 'password-reset/request'
|
|
: action === 'firebase-login' ? 'firebase/login'
|
|
: action;
|
|
const upstream = await fetch(`${baseUrl}/api/v1/auth/${endpoint}`, {
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json' },
|
|
body: JSON.stringify(await request.json()),
|
|
cache: 'no-store',
|
|
});
|
|
const body = await upstream.text();
|
|
const response = new NextResponse(upstream.status === 204 ? null : body, { status: upstream.status, headers: { 'content-type': upstream.headers.get('content-type') ?? 'application/json' } });
|
|
if (action === 'logout') {
|
|
response.cookies.delete('motorsignal_access_token');
|
|
response.cookies.delete('motorsignal_refresh_token');
|
|
response.cookies.delete('__Host-motorsignal_ops');
|
|
response.cookies.delete('motorsignal_ops');
|
|
} else if (upstream.ok) {
|
|
let payload: { access_token?: string; refresh_token?: string; ops_token?: string } = {};
|
|
try {
|
|
payload = JSON.parse(body) as { access_token?: string; refresh_token?: string; ops_token?: string };
|
|
} catch {
|
|
return response;
|
|
}
|
|
const cookieOptions = { httpOnly: true, secure: true, sameSite: 'lax' as const, path: '/', maxAge: 60 * 60 * 24 * 30 };
|
|
if (payload.access_token) response.cookies.set('motorsignal_access_token', payload.access_token, cookieOptions);
|
|
if (payload.refresh_token) response.cookies.set('motorsignal_refresh_token', payload.refresh_token, cookieOptions);
|
|
if (payload.ops_token) {
|
|
const opsCookieOptions = {
|
|
httpOnly: true,
|
|
secure: true,
|
|
sameSite: 'lax' as const,
|
|
path: '/',
|
|
maxAge: 60 * 60 * 8,
|
|
};
|
|
response.cookies.set('__Host-motorsignal_ops', payload.ops_token, opsCookieOptions);
|
|
response.cookies.set('motorsignal_ops', payload.ops_token, {
|
|
...opsCookieOptions,
|
|
secure: process.env.NODE_ENV === 'production',
|
|
});
|
|
}
|
|
}
|
|
return response;
|
|
}
|
|
|