chore: improve cookie helper functions (#5866)
This commit is contained in:
23
.vscode/launch.json
vendored
23
.vscode/launch.json
vendored
@@ -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}",
|
||||||
|
|||||||
@@ -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,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user