diff --git a/.vscode/launch.json b/.vscode/launch.json index 34e2968dd..653242779 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -41,6 +41,13 @@ "request": "launch", "type": "node-terminal" }, + { + "command": "node --no-deprecation test/dev.js auth", + "cwd": "${workspaceFolder}", + "name": "Run Dev Auth", + "request": "launch", + "type": "node-terminal" + }, { "command": "pnpm run dev plugin-cloud-storage", "cwd": "${workspaceFolder}", @@ -69,36 +76,26 @@ } }, { - "command": "pnpm run dev versions", + "command": "node --no-deprecation test/dev.js versions", "cwd": "${workspaceFolder}", "name": "Run Dev Versions", "request": "launch", "type": "node-terminal" }, { - "command": "pnpm run dev localization", + "command": "node --no-deprecation test/dev.js localization", "cwd": "${workspaceFolder}", "name": "Run Dev Localization", "request": "launch", "type": "node-terminal" }, { - "command": "pnpm run dev uploads", + "command": "node --no-deprecation test/dev.js uploads", "cwd": "${workspaceFolder}", "name": "Run Dev Uploads", "request": "launch", "type": "node-terminal" }, - { - "command": "PAYLOAD_BUNDLER=vite pnpm run dev fields", - "cwd": "${workspaceFolder}", - "name": "Run Dev Fields (Vite)", - "request": "launch", - "type": "node-terminal", - "env": { - "NODE_ENV": "production" - } - }, { "command": "pnpm run test:int live-preview", "cwd": "${workspaceFolder}", diff --git a/packages/payload/src/auth/cookies.ts b/packages/payload/src/auth/cookies.ts index 5d2adc302..31d6f24c0 100644 --- a/packages/payload/src/auth/cookies.ts +++ b/packages/payload/src/auth/cookies.ts @@ -1,5 +1,6 @@ import type { Payload } from '../index.js' import type { SanitizedCollectionConfig } from './../collections/config/types.js' + type CookieOptions = { domain?: string expires?: Date @@ -7,53 +8,108 @@ type CookieOptions = { maxAge?: number name: string path?: string + returnCookieAsObject: boolean sameSite?: 'Lax' | 'None' | 'Strict' secure?: boolean value?: string } -export const generateCookies = (cookies: CookieOptions[]): string => { - return cookies.map((options) => generateCookie(options)).join('; ') +type CookieObject = { + domain?: string + expires?: string + httpOnly?: boolean + maxAge?: number + name: string + path?: string + sameSite?: 'Lax' | 'None' | 'Strict' + secure?: boolean + value: string } -export const generateCookie = (args: CookieOptions): string => { - const { name, domain, expires, httpOnly, maxAge, path, sameSite, secure: secureArg, value } = args +export const generateCookie = ( + args: CookieOptions, +): ReturnCookieAsObject extends true ? CookieObject : string => { + const { + name, + domain, + expires, + httpOnly, + maxAge, + path, + returnCookieAsObject, + sameSite, + secure: secureArg, + value, + } = args let cookieString = `${name}=${value || ''}` + const cookieObject: CookieObject = { + name, + value, + } const secure = secureArg || sameSite === 'None' if (expires) { - cookieString += `; Expires=${expires.toUTCString()}` + if (returnCookieAsObject) { + cookieObject.expires = expires.toUTCString() + } else { + cookieString += `; Expires=${expires.toUTCString()}` + } } if (maxAge) { - cookieString += `; Max-Age=${maxAge}` + if (returnCookieAsObject) { + cookieObject.maxAge = maxAge + } else { + cookieString += `; Max-Age=${maxAge.toString()}` + } } if (domain) { - cookieString += `; Domain=${domain}` + if (returnCookieAsObject) { + cookieObject.domain = domain + } else { + cookieString += `; Domain=${domain}` + } } if (path) { - cookieString += `; Path=${path}` + if (returnCookieAsObject) { + cookieObject.path = path + } else { + cookieString += `; Path=${path}` + } } if (secure) { - cookieString += '; Secure' + if (returnCookieAsObject) { + cookieObject.secure = secure + } else { + cookieString += `; Secure=${secure}` + } } if (httpOnly) { - cookieString += '; HttpOnly' + if (returnCookieAsObject) { + cookieObject.httpOnly = httpOnly + } else { + cookieString += `; HttpOnly=${httpOnly}` + } } if (sameSite) { - cookieString += `; SameSite=${sameSite}` + if (returnCookieAsObject) { + cookieObject.sameSite = sameSite + } else { + cookieString += `; SameSite=${sameSite}` + } } - return cookieString + return (returnCookieAsObject ? cookieObject : cookieString) as ReturnCookieAsObject extends true + ? CookieObject + : string } - type GetCookieExpirationArgs = { /* The number of seconds until the cookie expires @@ -72,14 +128,17 @@ type GeneratePayloadCookieArgs = { collectionConfig: SanitizedCollectionConfig /* An instance of payload */ payload: Payload + /* The returnAs value */ + returnCookieAsObject?: boolean /* The token to be stored in the cookie */ token: string } -export const generatePayloadCookie = ({ +export const generatePayloadCookie = ({ collectionConfig, payload, + returnCookieAsObject = false, token, -}: GeneratePayloadCookieArgs): string => { +}: T): T['returnCookieAsObject'] extends true ? CookieObject : string => { const sameSite = typeof collectionConfig.auth.cookies.sameSite === 'string' ? collectionConfig.auth.cookies.sameSite @@ -87,22 +146,24 @@ export const generatePayloadCookie = ({ ? 'Strict' : undefined - return generateCookie({ + return generateCookie({ name: `${payload.config.cookiePrefix}-token`, domain: collectionConfig.auth.cookies.domain ?? undefined, expires: getCookieExpiration({ seconds: collectionConfig.auth.tokenExpiration }), httpOnly: true, path: '/', + returnCookieAsObject, sameSite, secure: collectionConfig.auth.cookies.secure, value: token, }) } -export const generateExpiredPayloadCookie = ({ +export const generateExpiredPayloadCookie = >({ collectionConfig, payload, -}: Omit): string => { + returnCookieAsObject = false, +}: T): T['returnCookieAsObject'] extends true ? CookieObject : string => { const sameSite = typeof collectionConfig.auth.cookies.sameSite === 'string' ? collectionConfig.auth.cookies.sameSite @@ -112,12 +173,13 @@ export const generateExpiredPayloadCookie = ({ const expires = new Date(Date.now() - 1000) - return generateCookie({ + return generateCookie({ name: `${payload.config.cookiePrefix}-token`, domain: collectionConfig.auth.cookies.domain ?? undefined, expires, httpOnly: true, path: '/', + returnCookieAsObject, sameSite, secure: collectionConfig.auth.cookies.secure, })