shelving 1.159.5 → 1.160.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/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.159.5",
14
+ "version": "1.160.1",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
package/util/jwt.d.ts CHANGED
@@ -1,13 +1,36 @@
1
1
  import { type Bytes, type PossibleBytes } from "./bytes.js";
2
2
  import type { Data } from "./data.js";
3
+ import { type PossibleDate } from "./date.js";
3
4
  import type { AnyCaller } from "./function.js";
5
+ export interface TokenClaims extends Data {
6
+ /**
7
+ * "Issued at" date (defaults to "now").
8
+ * - Not used for validation, but always set in the token payload.
9
+ * - Can be used to determine when the token was issued, and possibly revoke tokens issued before a certain date.
10
+ */
11
+ readonly iat?: PossibleDate;
12
+ /**
13
+ * "Not before" date.
14
+ * - When validating the token, tokens before this date will be rejected
15
+ */
16
+ readonly nbf?: PossibleDate;
17
+ /**
18
+ * Expiry in milliseconds (defaults to "30 days").
19
+ * - When validating the token, tokens after this date will be rejected
20
+ */
21
+ readonly exp?: number;
22
+ }
4
23
  /**
5
24
  * Encode a JWT and return the string token.
6
25
  * - Currently only supports HMAC SHA-512 signing.
7
26
  *
27
+ * @param claims The payload claims to include in the JWT.
28
+ * @param secret The secret key to sign the JWT with.
29
+ * @param expiry The expiry time in milliseconds (defaults to 30 days).
30
+ *
8
31
  * @throws ValueError If the input parameters, e.g. `secret` or `issuer`, are invalid.
9
32
  */
10
- export declare function encodeToken(claims: Data, secret: PossibleBytes): Promise<string>;
33
+ export declare function encodeToken({ nbf, iat, exp, ...claims }: TokenClaims, secret: PossibleBytes): Promise<string>;
11
34
  /** Parts that make up a JSON Web Token. */
12
35
  export type TokenData = {
13
36
  header: string;
package/util/jwt.js CHANGED
@@ -3,11 +3,11 @@ import { ValueError } from "../error/ValueError.js";
3
3
  import { decodeBase64URLBytes, decodeBase64URLString, encodeBase64URL } from "./base64.js";
4
4
  import { getBytes, requireBytes } from "./bytes.js";
5
5
  import { DAY, MINUTE, SECOND } from "./constants.js";
6
+ import { requireDate } from "./date.js";
6
7
  // Constants.
7
8
  const HASH = "SHA-512";
8
9
  const ALGORITHM = { name: "HMAC", hash: HASH };
9
10
  const HEADER = { alg: "HS512", typ: "JWT" };
10
- const EXPIRY_MS = DAY * 10;
11
11
  const SKEW_MS = MINUTE; // Allow 1 minute clock skew.
12
12
  const SECRET_BYTES = 64; // Minimum 64 bytes / 512 bits
13
13
  function _getKey(caller, secret, ...usages) {
@@ -23,15 +23,22 @@ function _getKey(caller, secret, ...usages) {
23
23
  * Encode a JWT and return the string token.
24
24
  * - Currently only supports HMAC SHA-512 signing.
25
25
  *
26
+ * @param claims The payload claims to include in the JWT.
27
+ * @param secret The secret key to sign the JWT with.
28
+ * @param expiry The expiry time in milliseconds (defaults to 30 days).
29
+ *
26
30
  * @throws ValueError If the input parameters, e.g. `secret` or `issuer`, are invalid.
27
31
  */
28
- export async function encodeToken(claims, secret) {
32
+ export async function encodeToken({ nbf = "now", iat = "now", exp = DAY * 30, ...claims }, secret) {
29
33
  // Encode header.
30
34
  const header = encodeBase64URL(JSON.stringify(HEADER));
31
35
  // Encode payload.
32
- const now = Math.floor(Date.now() / 1000);
33
- const exp = Math.floor(now + EXPIRY_MS / 1000);
34
- const payload = encodeBase64URL(JSON.stringify({ nbf: now, iat: now, exp, ...claims }));
36
+ const payload = encodeBase64URL(JSON.stringify({
37
+ nbf: Math.round(requireDate(nbf).getTime() / 1000), // By JWT convention, times are in seconds.
38
+ iat: Math.round(requireDate(iat).getTime() / 1000), // By JWT convention, times are in seconds.
39
+ exp: Math.round((Date.now() + exp) / 1000), // By JWT convention, times are in seconds.
40
+ ...claims,
41
+ }));
35
42
  // Create signature.
36
43
  const key = await _getKey(encodeToken, secret, "sign");
37
44
  const signature = encodeBase64URL(await crypto.subtle.sign("HMAC", key, requireBytes(`${header}.${payload}`)));
@@ -93,12 +100,10 @@ export async function verifyToken(token, secret, caller = verifyToken) {
93
100
  if (!isValid)
94
101
  throw new UnauthorizedError("JWT signature does not match", { received: token, caller });
95
102
  // Validate payload.
96
- const { nbf, iat, exp } = payloadData;
103
+ const { nbf, exp } = payloadData;
97
104
  const now = Date.now();
98
105
  if (typeof nbf === "number" && now < nbf * SECOND - SKEW_MS)
99
106
  throw new UnauthorizedError("JWT cannot be used yet", { received: payloadData, expected: now, caller });
100
- if (typeof iat === "number" && now < iat * SECOND - SKEW_MS)
101
- throw new UnauthorizedError("JWT not issued yet", { received: payloadData, expected: now, caller });
102
107
  if (typeof exp === "number" && now > exp * SECOND + SKEW_MS)
103
108
  throw new UnauthorizedError("JWT has expired", { received: payloadData, expected: now, caller });
104
109
  return payloadData;