@scayle/h3-session 0.5.1 → 0.6.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @scayle/h3-session
2
2
 
3
+ ## 0.6.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Disallow setting `name`, `path` or `domain` on the session cookie. Setting these properties would result in a new cookie rather than updating the existing cookie. It is now blocked to prevent unexpected behavior.
8
+
9
+ ## 0.6.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [Performance] Reduce the calls to `importKey` to improve performance.
14
+
3
15
  ## 0.5.1
4
16
 
5
17
  ### Patch Changes
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 SCAYLE GmbH
3
+ Copyright (c) 2025 SCAYLE GmbH
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.d.mts CHANGED
@@ -140,4 +140,5 @@ declare function validateConfig(config: H3SessionOptions): void;
140
140
  */
141
141
  declare function useSession(event: H3Event, config: H3SessionOptions): Promise<void>;
142
142
 
143
- export { type H3SessionOptions, type RawSession, Session, type SessionCookie, type SessionCookieOptions, type SessionDataT, type SessionMethods, type SessionStore, UnstorageSessionStore, signCookie, unsignCookie, useSession, validateConfig };
143
+ export { Session, UnstorageSessionStore, signCookie, unsignCookie, useSession, validateConfig };
144
+ export type { H3SessionOptions, RawSession, SessionCookie, SessionCookieOptions, SessionDataT, SessionMethods, SessionStore };
package/dist/index.d.ts CHANGED
@@ -140,4 +140,5 @@ declare function validateConfig(config: H3SessionOptions): void;
140
140
  */
141
141
  declare function useSession(event: H3Event, config: H3SessionOptions): Promise<void>;
142
142
 
143
- export { type H3SessionOptions, type RawSession, Session, type SessionCookie, type SessionCookieOptions, type SessionDataT, type SessionMethods, type SessionStore, UnstorageSessionStore, signCookie, unsignCookie, useSession, validateConfig };
143
+ export { Session, UnstorageSessionStore, signCookie, unsignCookie, useSession, validateConfig };
144
+ export type { H3SessionOptions, RawSession, SessionCookie, SessionCookieOptions, SessionDataT, SessionMethods, SessionStore };
package/dist/index.mjs CHANGED
@@ -3,6 +3,41 @@ import { getCookie, setCookie } from 'h3';
3
3
  import { defu } from 'defu';
4
4
  import { subtle, randomUUID } from 'uncrypto';
5
5
 
6
+ const _signKeys = {};
7
+ async function getCryptoSignKey(secret) {
8
+ if (_signKeys[secret]) {
9
+ return _signKeys[secret];
10
+ }
11
+ const encoder = new TextEncoder();
12
+ const keyUint8Array = encoder.encode(secret);
13
+ const cryptoKey = await subtle.importKey(
14
+ "raw",
15
+ keyUint8Array,
16
+ { name: "HMAC", hash: "SHA-256" },
17
+ false,
18
+ ["sign"]
19
+ );
20
+ _signKeys[secret] = cryptoKey;
21
+ return cryptoKey;
22
+ }
23
+ const _verifyKeys = {};
24
+ async function getCryptoVerifyKey(secret) {
25
+ if (_verifyKeys[secret]) {
26
+ return _verifyKeys[secret];
27
+ }
28
+ const encoder = new TextEncoder();
29
+ const keyUint8Array = encoder.encode(secret);
30
+ const cryptoKey = await subtle.importKey(
31
+ "raw",
32
+ keyUint8Array,
33
+ { name: "HMAC", hash: "SHA-256" },
34
+ false,
35
+ ["verify"]
36
+ );
37
+ _verifyKeys[secret] = cryptoKey;
38
+ return cryptoKey;
39
+ }
40
+
6
41
  class Session {
7
42
  #id;
8
43
  #store;
@@ -152,14 +187,7 @@ async function unsignCookie(value, secrets) {
152
187
  const messageUint8Array = encoder.encode(message);
153
188
  const signatureUint8Array = Uint8Array.from(Buffer.from(signature, "base64"));
154
189
  for (let i = 0; i < secrets.length; i++) {
155
- const keyUint8Array = encoder.encode(secrets[i]);
156
- const cryptoKey = await subtle.importKey(
157
- "raw",
158
- keyUint8Array,
159
- { name: "HMAC", hash: "SHA-256" },
160
- false,
161
- ["verify"]
162
- );
190
+ const cryptoKey = await getCryptoVerifyKey(secrets[i]);
163
191
  const result = await subtle.verify(
164
192
  "HMAC",
165
193
  cryptoKey,
@@ -181,14 +209,7 @@ async function signCookie(value, secret) {
181
209
  }
182
210
  const encoder = new TextEncoder();
183
211
  const messageUint8Array = encoder.encode(value);
184
- const keyUint8Array = encoder.encode(secret);
185
- const cryptoKey = await subtle.importKey(
186
- "raw",
187
- keyUint8Array,
188
- { name: "HMAC", hash: "SHA-256" },
189
- false,
190
- ["sign"]
191
- );
212
+ const cryptoKey = await getCryptoSignKey(secret);
192
213
  const signature = await subtle.sign("HMAC", cryptoKey, messageUint8Array);
193
214
  const b64Signature = Buffer.from(signature).toString("base64").replace(/=+$/, "");
194
215
  return `s:${value}.${b64Signature}`;
@@ -201,6 +222,7 @@ function validateConfig(config) {
201
222
  throw new Error("[h3-session] Session secret is required!");
202
223
  }
203
224
  }
225
+ const uniqueCookieAttributes = /* @__PURE__ */ new Set(["name", "path", "domain"]);
204
226
  async function useSession(event, config) {
205
227
  if (event.context.session) {
206
228
  return;
@@ -240,6 +262,11 @@ async function useSession(event, config) {
240
262
  await cookie.setSessionId(sid);
241
263
  return new Proxy(cookie, {
242
264
  set(target, property, value) {
265
+ if (uniqueCookieAttributes.has(property)) {
266
+ throw new Error(
267
+ `[h3-session] Cannot change ${property} on a session cookie!`
268
+ );
269
+ }
243
270
  target[property] = value;
244
271
  setCookie(event, sessionConfig.name, signedCookie, cookie);
245
272
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/h3-session",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
4
4
  "description": "Persistent sessions for h3",
5
5
  "author": "SCAYLE Commerce Engine",
6
6
  "license": "MIT",
@@ -8,6 +8,13 @@
8
8
  "access": "public",
9
9
  "registry": "https://registry.npmjs.org/"
10
10
  },
11
+ "repository": {
12
+ "url": "git+https://github.com/scayle/h3-session.git"
13
+ },
14
+ "keywords": [
15
+ "h3",
16
+ "session"
17
+ ],
11
18
  "exports": {
12
19
  ".": "./dist/index.mjs"
13
20
  },
@@ -21,10 +28,11 @@
21
28
  "verify-packaging": "attw --pack . --profile esm-only",
22
29
  "format": "dprint check",
23
30
  "format:fix": "dprint fmt",
24
- "lint": "eslint . --format gitlab",
31
+ "lint": "eslint .",
32
+ "lint:ci": "eslint . --format gitlab",
25
33
  "lint:fix": "eslint . --fix",
26
34
  "test": "vitest run",
27
- "test:ci": "vitest --run --coverage --reporter=default --reporter=junit --outputFile=./junit.xml",
35
+ "test:ci": "vitest --run --coverage --reporter=default --reporter=junit --outputFile=./coverage/junit.xml",
28
36
  "test:watch": "vitest watch",
29
37
  "typecheck": "tsc --noEmit -p tsconfig.json"
30
38
  },
@@ -37,18 +45,18 @@
37
45
  "unstorage": "^1.10.2"
38
46
  },
39
47
  "devDependencies": {
40
- "@arethetypeswrong/cli": "0.17.2",
41
- "@scayle/eslint-config-storefront": "4.4.0",
42
- "cookie-es": "1.2.2",
43
- "dprint": "0.48.0",
44
- "eslint": "9.17.0",
45
- "eslint-formatter-gitlab": "5.1.0",
46
- "h3": "1.13.0",
47
- "typescript": "5.7.2",
48
- "unbuild": "3.2.0",
49
- "vitest": "2.1.8"
48
+ "@arethetypeswrong/cli": "0.17.4",
49
+ "@scayle/eslint-config-storefront": "4.5.0",
50
+ "cookie-es": "2.0.0",
51
+ "dprint": "0.49.1",
52
+ "eslint": "9.25.1",
53
+ "eslint-formatter-gitlab": "6.0.0",
54
+ "h3": "1.15.1",
55
+ "typescript": "5.8.3",
56
+ "unbuild": "3.5.0",
57
+ "vitest": "3.1.2"
50
58
  },
51
59
  "volta": {
52
- "node": "22.12.0"
60
+ "node": "22.14.0"
53
61
  }
54
62
  }