diff --git a/frontend/src/app/api/contacts/[id]/route.ts b/frontend/src/app/api/contacts/[id]/route.ts index 3cf2369..fd08d05 100644 --- a/frontend/src/app/api/contacts/[id]/route.ts +++ b/frontend/src/app/api/contacts/[id]/route.ts @@ -1,15 +1,9 @@ import { NextResponse } from 'next/server'; -import { cookies } from 'next/headers'; -import { authCookieName } from '../../../../lib/auth'; - -const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001/api'; - -async function getToken() { - return (await cookies()).get(authCookieName)?.value; -} +import { SERVER_API_URL as API_URL } from '../../../../lib/server-api'; +import { getProxyAccessToken } from '../../../../lib/proxy-auth'; export async function GET(_: Request, { params }: { params: Promise<{ id: string }> }) { - const token = await getToken(); + const token = await getProxyAccessToken(); if (!token) { return NextResponse.json({ message: 'Unauthorized' }, { status: 401 }); } @@ -25,7 +19,7 @@ export async function GET(_: Request, { params }: { params: Promise<{ id: string } export async function PATCH(request: Request, { params }: { params: Promise<{ id: string }> }) { - const token = await getToken(); + const token = await getProxyAccessToken(); if (!token) { return NextResponse.json({ message: 'Unauthorized' }, { status: 401 }); } @@ -47,7 +41,7 @@ export async function PATCH(request: Request, { params }: { params: Promise<{ id } export async function DELETE(_: Request, { params }: { params: Promise<{ id: string }> }) { - const token = await getToken(); + const token = await getProxyAccessToken(); if (!token) { return NextResponse.json({ message: 'Unauthorized' }, { status: 401 }); } diff --git a/frontend/src/app/api/contacts/export/route.ts b/frontend/src/app/api/contacts/export/route.ts index 283cba2..858ab84 100644 --- a/frontend/src/app/api/contacts/export/route.ts +++ b/frontend/src/app/api/contacts/export/route.ts @@ -1,11 +1,9 @@ import { NextResponse } from 'next/server'; -import { cookies } from 'next/headers'; -import { authCookieName } from '../../../../lib/auth'; - -const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001/api'; +import { SERVER_API_URL as API_URL } from '../../../../lib/server-api'; +import { getProxyAccessToken } from '../../../../lib/proxy-auth'; export async function GET(request: Request) { - const token = (await cookies()).get(authCookieName)?.value; + const token = await getProxyAccessToken(); if (!token) { return NextResponse.json({ message: 'Unauthorized' }, { status: 401 }); } diff --git a/frontend/src/app/api/contacts/route.ts b/frontend/src/app/api/contacts/route.ts index 8ef2ee0..b387bba 100644 --- a/frontend/src/app/api/contacts/route.ts +++ b/frontend/src/app/api/contacts/route.ts @@ -1,8 +1,6 @@ import { NextResponse } from 'next/server'; -import { cookies } from 'next/headers'; -import { authCookieName } from '../../../lib/auth'; - -const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001/api'; +import { SERVER_API_URL as API_URL } from '../../../lib/server-api'; +import { getProxyAccessToken } from '../../../lib/proxy-auth'; function buildUrl(searchParams: URLSearchParams) { const query = searchParams.toString(); @@ -10,7 +8,7 @@ function buildUrl(searchParams: URLSearchParams) { } export async function GET(request: Request) { - const token = (await cookies()).get(authCookieName)?.value; + const token = await getProxyAccessToken(); if (!token) { return NextResponse.json({ message: 'Unauthorized' }, { status: 401 }); } @@ -28,7 +26,7 @@ export async function GET(request: Request) { } export async function POST(request: Request) { - const token = (await cookies()).get(authCookieName)?.value; + const token = await getProxyAccessToken(); if (!token) { return NextResponse.json({ message: 'Unauthorized' }, { status: 401 }); } diff --git a/frontend/src/lib/proxy-auth.ts b/frontend/src/lib/proxy-auth.ts new file mode 100644 index 0000000..04b72ef --- /dev/null +++ b/frontend/src/lib/proxy-auth.ts @@ -0,0 +1,58 @@ +import { cookies } from 'next/headers'; +import { authCookieName, refreshCookieName } from './auth'; +import { SERVER_API_URL } from './server-api'; + +const secureCookies = process.env.NODE_ENV === 'production'; + +type RefreshPayload = { + access_token: string; + refresh_token: string; + access_token_max_age_seconds?: number; + refresh_token_max_age_seconds?: number; +}; + +export async function getProxyAccessToken() { + const cookieStore = await cookies(); + const accessToken = cookieStore.get(authCookieName)?.value; + if (accessToken) { + return accessToken; + } + + const refreshToken = cookieStore.get(refreshCookieName)?.value; + if (!refreshToken) { + return null; + } + + const response = await fetch(`${SERVER_API_URL}/auth/refresh`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ refreshToken }), + cache: 'no-store', + }); + + if (!response.ok) { + return null; + } + + const payload = (await response.json()) as RefreshPayload; + if (!payload.access_token || !payload.refresh_token) { + return null; + } + + cookieStore.set(authCookieName, payload.access_token, { + httpOnly: true, + sameSite: 'lax', + secure: secureCookies, + path: '/', + maxAge: payload.access_token_max_age_seconds || 60 * 60 * 24, + }); + cookieStore.set(refreshCookieName, payload.refresh_token, { + httpOnly: true, + sameSite: 'lax', + secure: secureCookies, + path: '/', + maxAge: payload.refresh_token_max_age_seconds || 60 * 60 * 24 * 30, + }); + + return payload.access_token; +}