@devmoods/express-extras 0.38.0 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/csrf.d.ts CHANGED
@@ -1,12 +1,16 @@
1
1
  import { type RequestHandler, type Request, type Response } from 'express';
2
2
  export interface CsrfMiddlewareOptions {
3
3
  excempt?: boolean;
4
+ cookieName?: string;
5
+ sessionIdentifier?: (req: Request) => string;
4
6
  }
5
- export declare function csrf({ excempt, }?: CsrfMiddlewareOptions): RequestHandler;
6
- export declare function setCsrfIdentifier(identifier: string): void;
7
- export declare function useCsrfIdentifier(): string;
8
- export declare function setCsrfProtectionEnabled(enabled: boolean): void;
9
- export declare function useCsrfProtectionEnabled(): boolean | undefined;
7
+ interface CsrfOptions {
8
+ excempt: boolean;
9
+ cookieName: string;
10
+ sessionIdentifier: string;
11
+ }
12
+ export declare function csrf({ excempt, sessionIdentifier, cookieName, }?: CsrfMiddlewareOptions): RequestHandler;
13
+ export declare function setCsrfOptions(options: CsrfOptions): void;
10
14
  interface TokenOptions {
11
15
  overwrite?: boolean;
12
16
  }
package/dist/csrf.js CHANGED
@@ -3,31 +3,39 @@ import {} from 'express';
3
3
  import { setContext, useContext } from './context.js';
4
4
  import { Forbidden } from './errors.js';
5
5
  import { readEnv } from './utils.js';
6
- export function csrf({ excempt = false, } = {}) {
6
+ const ignoredMethods = new Set(['GET', 'HEAD', 'OPTIONS']);
7
+ const defaultCookieName = () => {
8
+ return process.env.NODE_ENV === 'production'
9
+ ? '__Host-devmoods.x-csrf-token'
10
+ : 'devmoods.x-csrf-token';
11
+ };
12
+ export function csrf({ excempt = false, sessionIdentifier, cookieName, } = {}) {
7
13
  return (req, res, next) => {
8
- setCsrfProtectionEnabled(!excempt);
14
+ const existingOptions = useCsrfOptions();
15
+ setCsrfOptions({
16
+ cookieName: cookieName ?? existingOptions.cookieName ?? defaultCookieName(),
17
+ sessionIdentifier: sessionIdentifier
18
+ ? sessionIdentifier(req)
19
+ : existingOptions.sessionIdentifier,
20
+ excempt,
21
+ });
9
22
  next();
10
23
  };
11
24
  }
12
- const CSRF_IDENTIFIER_CONTEXT_KEY = '@csrfRequestIdentifier';
13
- export function setCsrfIdentifier(identifier) {
14
- setContext(CSRF_IDENTIFIER_CONTEXT_KEY, identifier);
15
- }
16
- export function useCsrfIdentifier() {
17
- return useContext(CSRF_IDENTIFIER_CONTEXT_KEY) ?? '';
25
+ const CSRF_CONTEXT_KEY = '@csrfConfig';
26
+ export function setCsrfOptions(options) {
27
+ setContext(CSRF_CONTEXT_KEY, options);
18
28
  }
19
- const CSRF_REQUIRED = '@csrfProtectionEnabled';
20
- export function setCsrfProtectionEnabled(enabled) {
21
- setContext(CSRF_REQUIRED, enabled);
22
- }
23
- export function useCsrfProtectionEnabled() {
24
- return useContext(CSRF_REQUIRED);
29
+ function useCsrfOptions() {
30
+ return (useContext(CSRF_CONTEXT_KEY) || {
31
+ excempt: true,
32
+ cookieName: defaultCookieName(),
33
+ sessionIdentifier: '',
34
+ });
25
35
  }
26
- const ignoredMethods = new Set(['GET', 'HEAD', 'OPTIONS']);
27
- const cookieName = '__Host-devmoods.x-csrf-token';
28
36
  const generateTokenAndHash = (req, { overwrite = false } = {}) => {
29
37
  const secret = readEnv('CSRF_SECRET', '');
30
- const sessionIdentifier = useCsrfIdentifier();
38
+ const { sessionIdentifier, cookieName } = useCsrfOptions();
31
39
  const csrfCookie = req.cookies[cookieName];
32
40
  if (typeof csrfCookie === 'string' && !overwrite) {
33
41
  const [csrfToken, csrfTokenHash] = csrfCookie.split('|');
@@ -50,6 +58,7 @@ const generateTokenAndHash = (req, { overwrite = false } = {}) => {
50
58
  export const generateCsrfToken = (req, res, options = {}) => {
51
59
  const { csrfToken, csrfTokenHash } = generateTokenAndHash(req, options);
52
60
  const cookieContent = `${csrfToken}|${csrfTokenHash}`;
61
+ const { cookieName } = useCsrfOptions();
53
62
  res.cookie(cookieName, cookieContent, {
54
63
  sameSite: 'lax',
55
64
  path: '/',
@@ -68,6 +77,7 @@ const validateTokenHash = ({ csrfToken, csrfTokenHash, secret, sessionIdentifier
68
77
  return csrfTokenHash === expectedHash;
69
78
  };
70
79
  const validateRequest = (req) => {
80
+ const { cookieName, sessionIdentifier } = useCsrfOptions();
71
81
  const csrfCookie = req.cookies[cookieName];
72
82
  if (typeof csrfCookie !== 'string') {
73
83
  throw Forbidden('Missing CSRF cookie');
@@ -85,15 +95,15 @@ const validateRequest = (req) => {
85
95
  csrfToken: csrfTokenFromRequest,
86
96
  csrfTokenHash,
87
97
  secret,
88
- sessionIdentifier: useCsrfIdentifier(),
98
+ sessionIdentifier,
89
99
  })) {
90
100
  throw Forbidden('Invalid CSRF token');
91
101
  }
92
102
  };
93
103
  export function doubleCsrfProtection(req) {
94
104
  return async () => {
95
- const isCsrfProtectionEnabled = useCsrfProtectionEnabled();
96
- if (!isCsrfProtectionEnabled) {
105
+ const { excempt } = useCsrfOptions();
106
+ if (excempt) {
97
107
  return;
98
108
  }
99
109
  if (ignoredMethods.has(req.method)) {
package/dist/logger.js CHANGED
@@ -56,7 +56,7 @@ export function createLogger({ name, getContext, level = readEnv('LOG_LEVEL', 'I
56
56
  v: 1,
57
57
  });
58
58
  if (level >= logLevel) {
59
- output.write(payload + '\n');
59
+ output.write(`${payload}\n`);
60
60
  }
61
61
  }
62
62
  const api = {
package/dist/manage.js CHANGED
@@ -21,8 +21,9 @@ export function registerDefaultCommands(app) {
21
21
  .option('--openapi', 'Output OpenAPI schema')
22
22
  .option('--json', 'Output JSON')
23
23
  .action(cli.wrap(async (options) => {
24
- if (!app)
24
+ if (!app) {
25
25
  return;
26
+ }
26
27
  if (options.openapi) {
27
28
  console.error('Not implemented yet');
28
29
  return;
@@ -1,5 +1,6 @@
1
1
  import bodyParser from 'body-parser';
2
2
  import { type NextFunction, type RequestHandler, type Request, type Response } from 'express';
3
+ import { type CsrfMiddlewareOptions } from './csrf.js';
3
4
  type UrlencodedParser = typeof bodyParser.urlencoded;
4
5
  type JsonParser = typeof bodyParser.json;
5
6
  export declare const urlencodedParser: UrlencodedParser;
@@ -15,7 +16,7 @@ export interface BeforeMiddlewareOptions {
15
16
  cookies?: boolean;
16
17
  helmet?: boolean;
17
18
  json?: boolean;
18
- csrf?: boolean;
19
+ csrf?: boolean | CsrfMiddlewareOptions;
19
20
  allowUnsafeInlineScripts?: boolean;
20
21
  cspDirectives?: (defaults: BeforeMiddlewareCSPDirectives) => BeforeMiddlewareCSPDirectives;
21
22
  }
@@ -4,7 +4,7 @@ import cookieParser from 'cookie-parser';
4
4
  import {} from 'express';
5
5
  import helmet from 'helmet';
6
6
  import { withRequestContext } from './context.js';
7
- import { csrf, doubleCsrfProtection } from './csrf.js';
7
+ import { csrf, doubleCsrfProtection, } from './csrf.js';
8
8
  import { NotFound } from './errors.js';
9
9
  import { getLogger } from './logger.js';
10
10
  import { startTimer } from './utils.js';
@@ -18,7 +18,11 @@ export { asyncMiddleware as route };
18
18
  export function before({ cookies = false, helmet: useHelmet = true, json: useJson = true, csrf: useCsrf = false, allowUnsafeInlineScripts = false, cspDirectives = (x) => x, } = {}) {
19
19
  return compose([
20
20
  withRequestContext(),
21
- useCsrf && csrf(),
21
+ typeof useCsrf === 'object' && useCsrf != null
22
+ ? csrf(useCsrf)
23
+ : useCsrf
24
+ ? csrf()
25
+ : undefined,
22
26
  logRequestDuration(),
23
27
  useHelmet &&
24
28
  helmet({
package/dist/postgres.js CHANGED
@@ -88,8 +88,9 @@ export function createPostgres({ onError, url = readEnv('POSTGRES_URL'), ...conf
88
88
  * Wipe the database schema
89
89
  */
90
90
  async function clear(force = false) {
91
- if (!force)
91
+ if (!force) {
92
92
  return;
93
+ }
93
94
  return withConnection(async (connection) => {
94
95
  await connection.query('DROP SCHEMA public CASCADE');
95
96
  await connection.query(`CREATE SCHEMA public`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devmoods/express-extras",
3
- "version": "0.38.0",
3
+ "version": "0.39.0",
4
4
  "description": "Kubernetes-ready utilities for rapid Node.js application development",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,7 +18,7 @@
18
18
  ],
19
19
  "license": "ISC",
20
20
  "devDependencies": {
21
- "@devmoods/eslint-config": "^1.14.0",
21
+ "@devmoods/eslint-config": "^1.15.0",
22
22
  "@types/koa-compose": "^3.2.8",
23
23
  "@types/node": "22.8.6",
24
24
  "@types/supertest": "^6.0.2",