Skip to content

Commit

Permalink
feat: server action cookie options
Browse files Browse the repository at this point in the history
  • Loading branch information
renchris committed Oct 17, 2023
1 parent 9087531 commit 1326086
Showing 1 changed file with 44 additions and 35 deletions.
79 changes: 44 additions & 35 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,47 @@ type Password = PasswordsMap | string
type RequestType = IncomingMessage | Request
type ResponseType = Response | ServerResponse

/**
* {@link https://wicg.github.io/cookie-store/#dictdef-cookielistitem CookieListItem}
* as specified by W3C.
*/
interface CookieListItem
extends Pick<CookieSerializeOptions, 'domain' | 'path' | 'sameSite' | 'secure'> {
/** A string with the name of a cookie. */
name: string
/** A string containing the value of the cookie. */
value: string
/** A number of milliseconds or Date interface containing the expires of the cookie. */
expires?: CookieSerializeOptions['expires'] | number
}

/**
* Superset of {@link CookieListItem} extending it with
* the `httpOnly`, `maxAge` and `priority` properties.
*/
type ResponseCookie = CookieListItem &
Pick<CookieSerializeOptions, 'httpOnly' | 'maxAge' | 'priority'>

/**
* The high-level type definition of the .get() and .set() methods
* of { cookies() } from "next/headers"
*/
export interface ICookieHandler {
get: (name: string) => { name: string; value: string } | undefined
set: (name: string, value: string) => void
set: {
(name: string, value: string, cookie?: Partial<ResponseCookie>): void
(options: ResponseCookie): void
}
}

/**
* Set-Cookie Attributes do not include `encode`. We omit this from our `cookieOptions` type.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
* @see https://developer.chrome.com/docs/devtools/application/cookies/
*/
type CookieOptions = Omit<CookieSerializeOptions, 'encode'>

export interface IronSessionOptions {
/**
* The cookie name that will be used inside the browser. Make sure it's unique
Expand Down Expand Up @@ -57,7 +89,7 @@ export interface IronSessionOptions {
*
* @see https://github.com/jshttp/cookie#options-1
*/
cookieOptions?: CookieSerializeOptions
cookieOptions?: CookieOptions
}

type OverridableOptions = Pick<IronSessionOptions, 'cookieOptions' | 'ttl'>
Expand Down Expand Up @@ -134,22 +166,6 @@ function getServerActionCookie(cookieName: string, cookieHandler: ICookieHandler
return ''
}

function extractCookieComponents(
cookieValue: string
): { cookieName: string; cookieData: string } | null {
const components = cookieValue.split(';')
if (components.length > 0) {
const firstPart = components[0]
if (typeof firstPart === 'string') {
const parts = firstPart.trim().split('=')
if (parts.length === 2 && typeof parts[0] === 'string' && typeof parts[1] === 'string') {
return { cookieName: parts[0], cookieData: parts[1] }
}
}
}
return null
}

function setCookie(res: ResponseType, cookieValue: string): void {
if ('headers' in res && typeof res.headers.append === 'function') {
res.headers.append('set-cookie', cookieValue)
Expand All @@ -162,14 +178,6 @@ function setCookie(res: ResponseType, cookieValue: string): void {
;(res as ServerResponse).setHeader('set-cookie', [...existingSetCookie, cookieValue])
}

function setServerActionCookie(cookieValue: string, cookieHandler: ICookieHandler): void {
const extracted = extractCookieComponents(cookieValue)
if (extracted !== null) {
const { cookieName, cookieData } = extracted
cookieHandler.set(cookieName, cookieData)
}
}

export function createSealData(_crypto: Crypto = globalThis.crypto) {
return async function sealData(
data: unknown,
Expand Down Expand Up @@ -397,15 +405,19 @@ export function createGetServerActionIronSession(
value: async function save(saveOptions?: OverridableOptions) {
const mergedOptions = mergeOptions(userSessionOptions, saveOptions)
const seal = await sealData(session, { password: passwordsMap, ttl: mergedOptions.ttl })
const cookieValue = serialize(mergedOptions.cookieName, seal, mergedOptions.cookieOptions)

if (cookieValue.length > 4096) {
const cookieLength =
mergedOptions.cookieName.length +
seal.length +
JSON.stringify(mergedOptions.cookieOptions).length

if (cookieLength > 4096) {
throw new Error(
`iron-session: Cookie length is too big (${cookieValue.length} bytes), browsers will refuse it. Try to remove some data.`
`iron-session: Cookie length is too big (${cookieLength} bytes), browsers will refuse it. Try to remove some data.`
)
}

setServerActionCookie(cookieValue, cookieHandler)
cookieHandler.set(mergedOptions.cookieName, seal, mergedOptions.cookieOptions)
},
},

Expand All @@ -417,12 +429,9 @@ export function createGetServerActionIronSession(
delete session[key]
})
const mergedOptions = mergeOptions(userSessionOptions, destroyOptions)
const cookieValue = serialize(mergedOptions.cookieName, '', {
...mergedOptions.cookieOptions,
maxAge: 0,
})
const cookieOptions = { ...mergedOptions.cookieOptions, maxAge: 0 }

setServerActionCookie(cookieValue, cookieHandler)
cookieHandler.set(mergedOptions.cookieName, '', cookieOptions)
},
},
})
Expand Down

0 comments on commit 1326086

Please sign in to comment.