chore: improve cookie helper functions (#5866)

This commit is contained in:
Jarrod Flesch
2024-04-15 22:11:17 -04:00
committed by GitHub
parent 8178d57ab9
commit 3db0557b07
2 changed files with 91 additions and 32 deletions

23
.vscode/launch.json vendored
View File

@@ -41,6 +41,13 @@
"request": "launch", "request": "launch",
"type": "node-terminal" "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", "command": "pnpm run dev plugin-cloud-storage",
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
@@ -69,36 +76,26 @@
} }
}, },
{ {
"command": "pnpm run dev versions", "command": "node --no-deprecation test/dev.js versions",
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"name": "Run Dev Versions", "name": "Run Dev Versions",
"request": "launch", "request": "launch",
"type": "node-terminal" "type": "node-terminal"
}, },
{ {
"command": "pnpm run dev localization", "command": "node --no-deprecation test/dev.js localization",
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"name": "Run Dev Localization", "name": "Run Dev Localization",
"request": "launch", "request": "launch",
"type": "node-terminal" "type": "node-terminal"
}, },
{ {
"command": "pnpm run dev uploads", "command": "node --no-deprecation test/dev.js uploads",
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"name": "Run Dev Uploads", "name": "Run Dev Uploads",
"request": "launch", "request": "launch",
"type": "node-terminal" "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", "command": "pnpm run test:int live-preview",
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",

View File

@@ -1,5 +1,6 @@
import type { Payload } from '../index.js' import type { Payload } from '../index.js'
import type { SanitizedCollectionConfig } from './../collections/config/types.js' import type { SanitizedCollectionConfig } from './../collections/config/types.js'
type CookieOptions = { type CookieOptions = {
domain?: string domain?: string
expires?: Date expires?: Date
@@ -7,53 +8,108 @@ type CookieOptions = {
maxAge?: number maxAge?: number
name: string name: string
path?: string path?: string
returnCookieAsObject: boolean
sameSite?: 'Lax' | 'None' | 'Strict' sameSite?: 'Lax' | 'None' | 'Strict'
secure?: boolean secure?: boolean
value?: string value?: string
} }
export const generateCookies = (cookies: CookieOptions[]): string => { type CookieObject = {
return cookies.map((options) => generateCookie(options)).join('; ') 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 => { export const generateCookie = <ReturnCookieAsObject = boolean>(
const { name, domain, expires, httpOnly, maxAge, path, sameSite, secure: secureArg, value } = args args: CookieOptions,
): ReturnCookieAsObject extends true ? CookieObject : string => {
const {
name,
domain,
expires,
httpOnly,
maxAge,
path,
returnCookieAsObject,
sameSite,
secure: secureArg,
value,
} = args
let cookieString = `${name}=${value || ''}` let cookieString = `${name}=${value || ''}`
const cookieObject: CookieObject = {
name,
value,
}
const secure = secureArg || sameSite === 'None' const secure = secureArg || sameSite === 'None'
if (expires) { if (expires) {
if (returnCookieAsObject) {
cookieObject.expires = expires.toUTCString()
} else {
cookieString += `; Expires=${expires.toUTCString()}` cookieString += `; Expires=${expires.toUTCString()}`
} }
}
if (maxAge) { if (maxAge) {
cookieString += `; Max-Age=${maxAge}` if (returnCookieAsObject) {
cookieObject.maxAge = maxAge
} else {
cookieString += `; Max-Age=${maxAge.toString()}`
}
} }
if (domain) { if (domain) {
if (returnCookieAsObject) {
cookieObject.domain = domain
} else {
cookieString += `; Domain=${domain}` cookieString += `; Domain=${domain}`
} }
}
if (path) { if (path) {
if (returnCookieAsObject) {
cookieObject.path = path
} else {
cookieString += `; Path=${path}` cookieString += `; Path=${path}`
} }
}
if (secure) { if (secure) {
cookieString += '; Secure' if (returnCookieAsObject) {
cookieObject.secure = secure
} else {
cookieString += `; Secure=${secure}`
}
} }
if (httpOnly) { if (httpOnly) {
cookieString += '; HttpOnly' if (returnCookieAsObject) {
cookieObject.httpOnly = httpOnly
} else {
cookieString += `; HttpOnly=${httpOnly}`
}
} }
if (sameSite) { if (sameSite) {
if (returnCookieAsObject) {
cookieObject.sameSite = sameSite
} else {
cookieString += `; SameSite=${sameSite}` cookieString += `; SameSite=${sameSite}`
} }
return cookieString
} }
return (returnCookieAsObject ? cookieObject : cookieString) as ReturnCookieAsObject extends true
? CookieObject
: string
}
type GetCookieExpirationArgs = { type GetCookieExpirationArgs = {
/* /*
The number of seconds until the cookie expires The number of seconds until the cookie expires
@@ -72,14 +128,17 @@ type GeneratePayloadCookieArgs = {
collectionConfig: SanitizedCollectionConfig collectionConfig: SanitizedCollectionConfig
/* An instance of payload */ /* An instance of payload */
payload: Payload payload: Payload
/* The returnAs value */
returnCookieAsObject?: boolean
/* The token to be stored in the cookie */ /* The token to be stored in the cookie */
token: string token: string
} }
export const generatePayloadCookie = ({ export const generatePayloadCookie = <T extends GeneratePayloadCookieArgs>({
collectionConfig, collectionConfig,
payload, payload,
returnCookieAsObject = false,
token, token,
}: GeneratePayloadCookieArgs): string => { }: T): T['returnCookieAsObject'] extends true ? CookieObject : string => {
const sameSite = const sameSite =
typeof collectionConfig.auth.cookies.sameSite === 'string' typeof collectionConfig.auth.cookies.sameSite === 'string'
? collectionConfig.auth.cookies.sameSite ? collectionConfig.auth.cookies.sameSite
@@ -87,22 +146,24 @@ export const generatePayloadCookie = ({
? 'Strict' ? 'Strict'
: undefined : undefined
return generateCookie({ return generateCookie<T['returnCookieAsObject']>({
name: `${payload.config.cookiePrefix}-token`, name: `${payload.config.cookiePrefix}-token`,
domain: collectionConfig.auth.cookies.domain ?? undefined, domain: collectionConfig.auth.cookies.domain ?? undefined,
expires: getCookieExpiration({ seconds: collectionConfig.auth.tokenExpiration }), expires: getCookieExpiration({ seconds: collectionConfig.auth.tokenExpiration }),
httpOnly: true, httpOnly: true,
path: '/', path: '/',
returnCookieAsObject,
sameSite, sameSite,
secure: collectionConfig.auth.cookies.secure, secure: collectionConfig.auth.cookies.secure,
value: token, value: token,
}) })
} }
export const generateExpiredPayloadCookie = ({ export const generateExpiredPayloadCookie = <T extends Omit<GeneratePayloadCookieArgs, 'token'>>({
collectionConfig, collectionConfig,
payload, payload,
}: Omit<GeneratePayloadCookieArgs, 'token'>): string => { returnCookieAsObject = false,
}: T): T['returnCookieAsObject'] extends true ? CookieObject : string => {
const sameSite = const sameSite =
typeof collectionConfig.auth.cookies.sameSite === 'string' typeof collectionConfig.auth.cookies.sameSite === 'string'
? collectionConfig.auth.cookies.sameSite ? collectionConfig.auth.cookies.sameSite
@@ -112,12 +173,13 @@ export const generateExpiredPayloadCookie = ({
const expires = new Date(Date.now() - 1000) const expires = new Date(Date.now() - 1000)
return generateCookie({ return generateCookie<T['returnCookieAsObject']>({
name: `${payload.config.cookiePrefix}-token`, name: `${payload.config.cookiePrefix}-token`,
domain: collectionConfig.auth.cookies.domain ?? undefined, domain: collectionConfig.auth.cookies.domain ?? undefined,
expires, expires,
httpOnly: true, httpOnly: true,
path: '/', path: '/',
returnCookieAsObject,
sameSite, sameSite,
secure: collectionConfig.auth.cookies.secure, secure: collectionConfig.auth.cookies.secure,
}) })