Skip to content

Commit

Permalink
feat: expose per token cookie settings (#60)
Browse files Browse the repository at this point in the history
When calling `generateToken` the third options object parameter
can now take a cookieOptions property to override any of the initial
cookieOptions that were provided.

This commit also removes the forced httpOnly true option.

E.g. generateToken(req, res, { cookieOptions })
  • Loading branch information
psibean authored Apr 7, 2024
1 parent 290a912 commit 5e5ba9b
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 8 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ string;
<b>Default:</b> <code>"__Host-psifi.x-csrf-token"</code><br />
</p>

<p><b>Optional:</b> The name of the httpOnly cookie that will be used to track CSRF protection. If you change this it is recommend that you continue to use the <code>__Host-</code> or <code>__Secure-</code> <a target="_blank" href="developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie">security prefix</a>.</p>
<p><b>Optional:</b> The name of the cookie that will be used to track CSRF protection. If you change this it is recommend that you continue to use the <code>__Host-</code> or <code>__Secure-</code> <a target="_blank" href="developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie">security prefix</a>.</p>

<p><b>Change for development</b></p>

Expand Down Expand Up @@ -353,6 +353,7 @@ Used to customise the error response <code>statusCode</code>, the contained erro
request: Request,
response: Response,
{
cookieOptions?: CookieOptions, // overrides cookieOptions previously configured just for this call
overwrite?: boolean, // Set to true to force a new token to be generated
validateOnReuse?: boolean, // Set to false to generate a new token if token re-use is invalid
} // optional
Expand Down
20 changes: 16 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function doubleCsrf({
sameSite = "lax",
path = "/",
secure = true,
httpOnly = true,
...remainingCookieOptions
} = {},
size = 64,
Expand All @@ -35,10 +36,11 @@ export function doubleCsrf({
} = {},
}: DoubleCsrfConfigOptions): DoubleCsrfUtilities {
const ignoredMethodsSet = new Set(ignoredMethods);
const cookieOptions = {
const defaultCookieOptions = {
sameSite,
path,
secure,
httpOnly,
...remainingCookieOptions,
};

Expand All @@ -48,7 +50,10 @@ export function doubleCsrf({

const generateTokenAndHash = (
req: Request,
{ overwrite, validateOnReuse }: GenerateCsrfTokenConfig,
{
overwrite,
validateOnReuse,
}: Omit<GenerateCsrfTokenConfig, "cookieOptions">,
) => {
const getSecretResult = getSecret(req);
const possibleSecrets = Array.isArray(getSecretResult)
Expand Down Expand Up @@ -91,14 +96,21 @@ export function doubleCsrf({
const generateToken: CsrfTokenCreator = (
req: Request,
res: Response,
{ overwrite = false, validateOnReuse = true } = {},
{
cookieOptions = defaultCookieOptions,
overwrite = false,
validateOnReuse = true,
} = {},
) => {
const { csrfToken, csrfTokenHash } = generateTokenAndHash(req, {
overwrite,
validateOnReuse,
});
const cookieContent = `${csrfToken}|${csrfTokenHash}`;
res.cookie(cookieName, cookieContent, { ...cookieOptions, httpOnly: true });
res.cookie(cookieName, cookieContent, {
...defaultCookieOptions,
...cookieOptions,
});
return csrfToken;
};

Expand Down
7 changes: 4 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { HttpError } from "http-errors";

export type SameSiteType = boolean | "lax" | "strict" | "none";
export type TokenRetriever = (req: Request) => string | null | undefined;
export type DoubleCsrfCookieOptions = Omit<CookieOptions, "httpOnly">;
export type CsrfTokenCookieOverrides = Omit<CookieOptions, "signed">;
declare module "http" {
interface IncomingHttpHeaders {
"x-csrf-token"?: string | undefined;
Expand Down Expand Up @@ -48,7 +48,7 @@ export type CsrfCookieSetter = (
res: Response,
name: string,
value: string,
options: DoubleCsrfCookieOptions,
options: CookieOptions,
) => void;
export type CsrfTokenCreator = (
req: Request,
Expand All @@ -64,6 +64,7 @@ export type CsrfErrorConfigOptions = Partial<CsrfErrorConfig>;
export type GenerateCsrfTokenConfig = {
overwrite: boolean;
validateOnReuse: boolean;
cookieOptions: CsrfTokenCookieOverrides;
};
export type GenerateCsrfTokenOptions = Partial<GenerateCsrfTokenConfig>;
export interface DoubleCsrfConfig {
Expand Down Expand Up @@ -103,7 +104,7 @@ export interface DoubleCsrfConfig {
* The options for HTTPOnly cookie that will be set on the response.
* @default { sameSite: "lax", path: "/", secure: true }
*/
cookieOptions: DoubleCsrfCookieOptions;
cookieOptions: CookieOptions;

/**
* The methods that will be ignored by the middleware.
Expand Down

0 comments on commit 5e5ba9b

Please sign in to comment.