From 2aae7435f87ee562a672e9b8ab879bb5efd9b61a Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Tue, 3 Dec 2024 15:28:30 +1100 Subject: [PATCH] fix: auth cookies across iframes (#1501) --- .../src/app/embed/sign/[[...url]]/page.tsx | 2 +- apps/web/src/middleware.ts | 7 +-- packages/lib/next-auth/auth-options.ts | 51 +++++++++++++++++++ 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/embed/sign/[[...url]]/page.tsx b/apps/web/src/app/embed/sign/[[...url]]/page.tsx index 0f3351a83..c07cd0be3 100644 --- a/apps/web/src/app/embed/sign/[[...url]]/page.tsx +++ b/apps/web/src/app/embed/sign/[[...url]]/page.tsx @@ -80,7 +80,7 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen return ( ); } diff --git a/apps/web/src/middleware.ts b/apps/web/src/middleware.ts index 09130cf7b..25de9debb 100644 --- a/apps/web/src/middleware.ts +++ b/apps/web/src/middleware.ts @@ -78,13 +78,14 @@ async function middleware(req: NextRequest): Promise { if (req.nextUrl.pathname.startsWith('/embed')) { const res = NextResponse.next(); + const origin = req.headers.get('Origin') ?? '*'; + // Allow third parties to iframe the document. res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); - res.headers.set('Access-Control-Allow-Origin', '*'); - res.headers.set('Content-Security-Policy', 'frame-ancestors *'); + res.headers.set('Access-Control-Allow-Origin', origin); + res.headers.set('Content-Security-Policy', `frame-ancestors ${origin}`); res.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin'); res.headers.set('X-Content-Type-Options', 'nosniff'); - res.headers.set('X-Frame-Options', 'ALLOW-ALL'); return res; } diff --git a/packages/lib/next-auth/auth-options.ts b/packages/lib/next-auth/auth-options.ts index 899a1b1b9..f5b0779cf 100644 --- a/packages/lib/next-auth/auth-options.ts +++ b/packages/lib/next-auth/auth-options.ts @@ -26,6 +26,9 @@ import { extractNextAuthRequestMetadata } from '../universal/extract-request-met import { getAuthenticatorOptions } from '../utils/authenticator'; import { ErrorCode } from './error-codes'; +const useSecureCookies = process.env.NODE_ENV === 'production'; +const cookiePrefix = useSecureCookies ? '__Secure-' : ''; + export const NEXT_AUTH_OPTIONS: AuthOptions = { adapter: PrismaAdapter(prisma), secret: process.env.NEXTAUTH_SECRET ?? 'secret', @@ -431,5 +434,53 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = { return true; }, }, + cookies: { + sessionToken: { + name: `${cookiePrefix}next-auth.session-token`, + options: { + httpOnly: true, + sameSite: 'none', + path: '/', + secure: useSecureCookies, + }, + }, + callbackUrl: { + name: `${cookiePrefix}next-auth.callback-url`, + options: { + sameSite: 'none', + path: '/', + secure: useSecureCookies, + }, + }, + csrfToken: { + // Default to __Host- for CSRF token for additional protection if using useSecureCookies + // NB: The `__Host-` prefix is stricter than the `__Secure-` prefix. + name: `${cookiePrefix}next-auth.csrf-token`, + options: { + httpOnly: true, + sameSite: 'none', + path: '/', + secure: useSecureCookies, + }, + }, + pkceCodeVerifier: { + name: `${cookiePrefix}next-auth.pkce.code_verifier`, + options: { + httpOnly: true, + sameSite: 'none', + path: '/', + secure: useSecureCookies, + }, + }, + state: { + name: `${cookiePrefix}next-auth.state`, + options: { + httpOnly: true, + sameSite: 'none', + path: '/', + secure: useSecureCookies, + }, + }, + }, // Note: `events` are handled in `apps/web/src/pages/api/auth/[...nextauth].ts` to allow access to the request. };