From 26196ceb3058b9f38b6c8ba799937457f7941b1c Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Sat, 11 Oct 2025 18:53:19 +0000 Subject: [PATCH 1/4] fix: add proper cookie options to OAuth state cookie The OAuth state cookie was being set without explicit options, which caused it to be lost during OAuth redirects in some browsers due to default SameSite policies. This fix adds: - httpOnly: true - Prevents XSS attacks - secure: true (production) - HTTPS only in production - sameSite: 'lax' - Critical: allows cookie during OAuth redirects - maxAge: 600 seconds - OAuth should complete within 10 minutes - path: '/' - Available across all paths Fixes state mismatch errors during OAuth authentication flow. Related issue: OAuth state cookie being lost during Okta redirects causing 'state mismatch' errors on first authentication attempt. --- src/runtime/server/lib/utils.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/runtime/server/lib/utils.ts b/src/runtime/server/lib/utils.ts index 699eda29..0bb26f79 100644 --- a/src/runtime/server/lib/utils.ts +++ b/src/runtime/server/lib/utils.ts @@ -212,6 +212,12 @@ export async function handleState(event: H3Event) { } state = encodeBase64Url(getRandomBytes(8)) - setCookie(event, 'nuxt-auth-state', state) + setCookie(event, 'nuxt-auth-state', state, { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + maxAge: 60 * 10, // 10 minutes + path: '/', + }) return state } From d18740ba00d022d2098c65543f7c62cfe749671a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Biseth?= Date: Sat, 11 Oct 2025 19:53:14 +0000 Subject: [PATCH 2/4] fix: update cookie security settings for development environment --- src/runtime/server/lib/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/server/lib/utils.ts b/src/runtime/server/lib/utils.ts index 0bb26f79..0275771c 100644 --- a/src/runtime/server/lib/utils.ts +++ b/src/runtime/server/lib/utils.ts @@ -214,7 +214,7 @@ export async function handleState(event: H3Event) { state = encodeBase64Url(getRandomBytes(8)) setCookie(event, 'nuxt-auth-state', state, { httpOnly: true, - secure: process.env.NODE_ENV === 'production', + secure: process.env.NODE_ENV !== 'development', sameSite: 'lax', maxAge: 60 * 10, // 10 minutes path: '/', From da932a80093fee857ce5851f4c0e38fc6f67c899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Biseth?= Date: Sat, 11 Oct 2025 20:01:05 +0000 Subject: [PATCH 3/4] fix: enhance PKCE cookie security settings --- src/runtime/server/lib/utils.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/runtime/server/lib/utils.ts b/src/runtime/server/lib/utils.ts index 0275771c..f73e4ad1 100644 --- a/src/runtime/server/lib/utils.ts +++ b/src/runtime/server/lib/utils.ts @@ -190,7 +190,13 @@ export async function handlePkceVerifier(event: H3Event) { // Create new verifier verifier = encodeBase64Url(getRandomBytes()) - setCookie(event, 'nuxt-auth-pkce', verifier) + setCookie(event, 'nuxt-auth-pkce', verifier, { + httpOnly: true, + secure: process.env.NODE_ENV !== 'development', + sameSite: 'lax', + maxAge: 60 * 10, // 10 minutes + path: '/', + }) // Get pkce const encodedPkce = new TextEncoder().encode(verifier) From 8f9134e8cbb7bf30887a93c712681f082e60e1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Biseth?= Date: Sat, 11 Oct 2025 21:46:57 +0000 Subject: [PATCH 4/4] fix: standardize PKCE cookie settings and expiration time --- src/runtime/server/lib/utils.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/runtime/server/lib/utils.ts b/src/runtime/server/lib/utils.ts index f73e4ad1..ae566162 100644 --- a/src/runtime/server/lib/utils.ts +++ b/src/runtime/server/lib/utils.ts @@ -7,6 +7,12 @@ import { subtle, getRandomValues } from 'uncrypto' import type { OAuthProvider, OnError } from '#auth-utils' import { createError } from '#imports' +// Determine if we are in development mode +const isDevelopment = process.env.NODE_ENV === 'development' + +// OAuth cookie expiration time (10 minutes in seconds) +const OAUTH_COOKIE_MAX_AGE = 60 * 10 + export function getOAuthRedirectURL(event: H3Event): string { const requestURL = getRequestURL(event) @@ -192,9 +198,9 @@ export async function handlePkceVerifier(event: H3Event) { verifier = encodeBase64Url(getRandomBytes()) setCookie(event, 'nuxt-auth-pkce', verifier, { httpOnly: true, - secure: process.env.NODE_ENV !== 'development', + secure: !isDevelopment, sameSite: 'lax', - maxAge: 60 * 10, // 10 minutes + maxAge: OAUTH_COOKIE_MAX_AGE, path: '/', }) @@ -220,9 +226,9 @@ export async function handleState(event: H3Event) { state = encodeBase64Url(getRandomBytes(8)) setCookie(event, 'nuxt-auth-state', state, { httpOnly: true, - secure: process.env.NODE_ENV !== 'development', + secure: !isDevelopment, sameSite: 'lax', - maxAge: 60 * 10, // 10 minutes + maxAge: OAUTH_COOKIE_MAX_AGE, path: '/', }) return state