From 3feb36ce7eecce836fd236bcc506d39eff5d40ad Mon Sep 17 00:00:00 2001 From: psibean Date: Tue, 2 Apr 2024 21:22:51 +1030 Subject: [PATCH] feat: allow customizable error Exposes the statusCode, message, and code parameters for the error initialisation --- README.md | 25 ++++++++++++++++++++++++- src/index.ts | 9 +++++++-- src/tests/doublecsrf.test.ts | 10 ++++++++++ src/tests/testsuite.ts | 12 ++++++++++++ src/types.ts | 12 ++++++++++++ 5 files changed, 65 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4f0fa58..86b6f74 100644 --- a/README.md +++ b/README.md @@ -311,6 +311,29 @@ number;

The size in bytes of the tokens that will be generated, if you plan on re-generating tokens consider dropping this to 32.

+

errorConfig

+ +```ts +statusCode?: number; +message?: string; +code?: string | undefined; +``` + +

+ Optional
+ Default:
+

+ +```ts +{ + statusCode: 403, + message: "invalid csrf token", + code: "EBADCSRFTOKEN" +} +``` + +Used to customise the error response statusCode, the contained error message, and it's code, the error is constructed via createHttpError. The default values match that of csurf for convenience. +

Utilities

Below is the documentation for what doubleCsrf returns.

@@ -366,7 +389,7 @@ req.csrfToken(false, false); // same as generateToken(req, res, false, false);

invalidCsrfTokenError

-

This is the error instance that gets passed as an error to the next call, by default this will be handled by the default error handler.

+

This is the error instance that gets passed as an error to the next call, by default this will be handled by the default error handler. This error is customizable via the errorConfig.

validateToken

diff --git a/src/index.ts b/src/index.ts index fd1a157..abfe2ab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,6 +26,11 @@ export function doubleCsrf({ size = 64, ignoredMethods = ["GET", "HEAD", "OPTIONS"], getTokenFromRequest = (req) => req.headers["x-csrf-token"], + errorConfig: { + statusCode = 403, + message = "invalid csrf token", + code = "EBADCSRFTOKEN", + } = {}, }: DoubleCsrfConfigOptions): DoubleCsrfUtilities { const ignoredMethodsSet = new Set(ignoredMethods); const cookieOptions = { @@ -35,8 +40,8 @@ export function doubleCsrf({ ...remainingCookieOptions, }; - const invalidCsrfTokenError = createHttpError(403, "invalid csrf token", { - code: "EBADCSRFTOKEN", + const invalidCsrfTokenError = createHttpError(statusCode, message, { + code: code, }); const generateTokenAndHash = ( diff --git a/src/tests/doublecsrf.test.ts b/src/tests/doublecsrf.test.ts index 0fa90e5..763d1c3 100644 --- a/src/tests/doublecsrf.test.ts +++ b/src/tests/doublecsrf.test.ts @@ -17,6 +17,11 @@ createTestSuite("csrf-csrf unsigned, single secret", { createTestSuite("csrf-csrf signed, single secret", { cookieOptions: { signed: true }, getSecret: getSingleSecret, + errorConfig: { + statusCode: 400, + message: "NOT GOOD", + code: "BADTOKEN", + }, }); createTestSuite("csrf-csrf signed with custom options, single secret", { getSecret: getSingleSecret, @@ -37,6 +42,11 @@ createTestSuite("csrf-csrf signed with custom options, multiple secrets", { cookieOptions: { signed: true, sameSite: "strict" }, size: 128, cookieName: "__Host.test-the-thing.token", + errorConfig: { + statusCode: 401, + message: "GO AWAY", + code: "FAKE", + }, }); describe("csrf-csrf token-rotation", () => { diff --git a/src/tests/testsuite.ts b/src/tests/testsuite.ts index 6cadce8..0f179f9 100644 --- a/src/tests/testsuite.ts +++ b/src/tests/testsuite.ts @@ -43,6 +43,11 @@ export const createTestSuite: CreateTestsuite = (name, doubleCsrfOptions) => { secure = true, sameSite = "lax", } = {}, + errorConfig = { + statusCode: 403, + message: "invalid csrf token", + code: "EBADCSRFTOKEN", + }, } = doubleCsrfOptions; const generateMocksWithTokenIntenral = () => @@ -53,6 +58,13 @@ export const createTestSuite: CreateTestsuite = (name, doubleCsrfOptions) => { validateRequest, }); + it("should initialize error via config options", () => { + console.log(invalidCsrfTokenError); + assert.equal(errorConfig.message, invalidCsrfTokenError.message); + assert.equal(errorConfig.statusCode, invalidCsrfTokenError.statusCode); + assert.equal(errorConfig.code, invalidCsrfTokenError.code); + }); + describe("generateToken", () => { it("should attach both a token and its hash to the response and return a token", () => { const { mockRequest, decodedCookieValue, setCookie } = diff --git a/src/types.ts b/src/types.ts index d737e9c..142d90c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -54,6 +54,12 @@ export type CsrfTokenCreator = ( ovewrite?: boolean, validateOnReuse?: boolean, ) => string; +export type CsrfErrorConfig = { + statusCode: number; + message: string; + code: string | undefined; +}; +export type CsrfErrorConfigOptions = Partial; export interface DoubleCsrfConfig { /** @@ -114,6 +120,12 @@ export interface DoubleCsrfConfig { * ``` */ getTokenFromRequest: TokenRetriever; + + /** + * Configuration for the error that is thrown any time XSRF token validation fails. + * @default { statusCode: 403, message: "invalid csrf token", code: "EBADCSRFTOKEN" } + */ + errorConfig: CsrfErrorConfigOptions; } export interface DoubleCsrfUtilities {