@zudojs/auth 0.1.1 → 1.1.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.
Files changed (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +224 -19
  3. package/dist/authErrors/authError.base.d.ts +50 -16
  4. package/dist/authErrors/authError.base.js +94 -18
  5. package/dist/authErrors/index.d.ts +1 -1
  6. package/dist/authErrors/index.js +1 -1
  7. package/dist/authPassword/authPassword.core.d.ts +25 -2
  8. package/dist/authPassword/authPassword.core.js +50 -2
  9. package/dist/authPassword/index.d.ts +1 -1
  10. package/dist/authPassword/index.js +1 -1
  11. package/dist/authProvider/authAttempt.memory.d.ts +21 -0
  12. package/dist/authProvider/authAttempt.memory.js +91 -0
  13. package/dist/authProvider/authProvider.core.d.ts +63 -9
  14. package/dist/authProvider/authProvider.core.js +221 -37
  15. package/dist/authProvider/index.d.ts +2 -1
  16. package/dist/authProvider/index.js +1 -0
  17. package/dist/authSession/authSession.core.d.ts +20 -3
  18. package/dist/authSession/authSession.core.js +71 -21
  19. package/dist/authToken/authToken.core.d.ts +27 -1
  20. package/dist/authToken/authToken.core.js +31 -1
  21. package/dist/authToken/authToken.encoding.d.ts +43 -0
  22. package/dist/authToken/authToken.encoding.js +74 -0
  23. package/dist/authToken/authToken.revocation.d.ts +10 -2
  24. package/dist/authToken/authToken.revocation.js +42 -7
  25. package/dist/authToken/authToken.signing.d.ts +24 -1
  26. package/dist/authToken/authToken.signing.js +117 -28
  27. package/dist/authToken/jwt.namespace.d.ts +18 -8
  28. package/dist/authToken/jwt.namespace.js +11 -1
  29. package/dist/authTypes/authAttempt.type.d.ts +65 -0
  30. package/dist/authTypes/authAttempt.type.js +7 -0
  31. package/dist/authTypes/authCredentials.type.d.ts +26 -0
  32. package/dist/authTypes/authCredentials.type.js +11 -0
  33. package/dist/authTypes/authSession.type.d.ts +31 -3
  34. package/dist/authTypes/authSession.type.js +17 -1
  35. package/dist/authTypes/authToken.type.d.ts +29 -1
  36. package/dist/authTypes/authUser.type.d.ts +12 -0
  37. package/dist/authTypes/authUser.type.js +17 -1
  38. package/dist/authTypes/index.d.ts +5 -4
  39. package/dist/authTypes/index.js +5 -4
  40. package/dist/authUtils/authUtils.helper.d.ts +31 -10
  41. package/dist/authUtils/authUtils.helper.js +70 -31
  42. package/package.json +27 -16
  43. package/dist/.tsbuildinfo +0 -1
  44. package/dist/authErrors/authError.base.d.ts.map +0 -1
  45. package/dist/authErrors/authError.base.js.map +0 -1
  46. package/dist/authErrors/index.d.ts.map +0 -1
  47. package/dist/authErrors/index.js.map +0 -1
  48. package/dist/authPassword/authPassword.core.d.ts.map +0 -1
  49. package/dist/authPassword/authPassword.core.js.map +0 -1
  50. package/dist/authPassword/index.d.ts.map +0 -1
  51. package/dist/authPassword/index.js.map +0 -1
  52. package/dist/authProvider/authProvider.core.d.ts.map +0 -1
  53. package/dist/authProvider/authProvider.core.js.map +0 -1
  54. package/dist/authProvider/index.d.ts.map +0 -1
  55. package/dist/authProvider/index.js.map +0 -1
  56. package/dist/authSession/authSession.core.d.ts.map +0 -1
  57. package/dist/authSession/authSession.core.js.map +0 -1
  58. package/dist/authSession/index.d.ts.map +0 -1
  59. package/dist/authSession/index.js.map +0 -1
  60. package/dist/authToken/authToken.core.d.ts.map +0 -1
  61. package/dist/authToken/authToken.core.js.map +0 -1
  62. package/dist/authToken/authToken.revocation.d.ts.map +0 -1
  63. package/dist/authToken/authToken.revocation.js.map +0 -1
  64. package/dist/authToken/authToken.signing.d.ts.map +0 -1
  65. package/dist/authToken/authToken.signing.js.map +0 -1
  66. package/dist/authToken/index.d.ts.map +0 -1
  67. package/dist/authToken/index.js.map +0 -1
  68. package/dist/authToken/jwt.namespace.d.ts.map +0 -1
  69. package/dist/authToken/jwt.namespace.js.map +0 -1
  70. package/dist/authTypes/authRbac.type.d.ts.map +0 -1
  71. package/dist/authTypes/authRbac.type.js.map +0 -1
  72. package/dist/authTypes/authSession.type.d.ts.map +0 -1
  73. package/dist/authTypes/authSession.type.js.map +0 -1
  74. package/dist/authTypes/authStrategy.type.d.ts +0 -83
  75. package/dist/authTypes/authStrategy.type.d.ts.map +0 -1
  76. package/dist/authTypes/authStrategy.type.js +0 -7
  77. package/dist/authTypes/authStrategy.type.js.map +0 -1
  78. package/dist/authTypes/authToken.type.d.ts.map +0 -1
  79. package/dist/authTypes/authToken.type.js.map +0 -1
  80. package/dist/authTypes/authUser.type.d.ts.map +0 -1
  81. package/dist/authTypes/authUser.type.js.map +0 -1
  82. package/dist/authTypes/index.d.ts.map +0 -1
  83. package/dist/authTypes/index.js.map +0 -1
  84. package/dist/authUtils/authUtils.helper.d.ts.map +0 -1
  85. package/dist/authUtils/authUtils.helper.js.map +0 -1
  86. package/dist/authUtils/index.d.ts.map +0 -1
  87. package/dist/authUtils/index.js.map +0 -1
  88. package/dist/index.d.ts.map +0 -1
  89. package/dist/index.js.map +0 -1
@@ -4,74 +4,113 @@
4
4
  * @module authUtils
5
5
  */
6
6
  import { randomBytes } from "node:crypto";
7
+ import { decodeJsonSegment, splitToken, } from "../authToken/authToken.encoding.js";
8
+ /**
9
+ * Maximum accepted length of an `Authorization` header value.
10
+ * Sized to comfortably hold `Bearer ` plus a maximum-length JWT.
11
+ */
12
+ const MAX_AUTHORIZATION_LENGTH = 8256;
13
+ /** Maximum accepted length of a `Cookie` header value. */
14
+ const MAX_COOKIE_HEADER_LENGTH = 8192;
15
+ /** Maximum number of cookie pairs parsed from one header. */
16
+ const MAX_COOKIE_PAIRS = 100;
17
+ /**
18
+ * `Bearer <token>` — the scheme is matched case-insensitively per RFC 7235
19
+ * §2.1, and surrounding/extra whitespace is tolerated.
20
+ */
21
+ const BEARER_PATTERN = /^\s*bearer[ \t]+(\S+)\s*$/i;
7
22
  /**
8
23
  * Parse a Bearer token from an Authorization header.
9
24
  *
25
+ * Accepts `unknown` on purpose: this sits on the HTTP trust boundary, where
26
+ * a duplicated header gives `string[]` and adapter layers routinely pass
27
+ * through values they have not narrowed. Anything that is not a plausible
28
+ * header value is `null`, never a thrown `TypeError`.
29
+ *
10
30
  * @param authorization - Raw Authorization header value
11
31
  * @returns The token string, or null if not a Bearer token
12
32
  */
13
33
  export function parseBearerToken(authorization) {
14
- if (!authorization)
34
+ if (typeof authorization !== "string")
15
35
  return null;
16
- const parts = authorization.split(" ");
17
- if (parts.length !== 2 || parts[0] !== "Bearer")
36
+ if (authorization.length > MAX_AUTHORIZATION_LENGTH)
18
37
  return null;
19
- return parts[1] ?? null;
38
+ const match = BEARER_PATTERN.exec(authorization);
39
+ return match?.[1] ?? null;
20
40
  }
21
41
  /**
22
42
  * Parse cookie string into a key-value map.
23
43
  *
24
- * @param cookie - Raw Cookie header value
25
- * @returns Parsed cookies
44
+ * The returned object has a `null` prototype, so a cookie named
45
+ * `constructor`, `hasOwnProperty` or `__proto__` cannot shadow or confuse an
46
+ * inherited member for the caller. Oversized headers and cookie counts are
47
+ * capped rather than allocated.
48
+ *
49
+ * @param cookie - Raw Cookie header value (any type; see
50
+ * {@link parseBearerToken} for why)
51
+ * @returns Parsed cookies — an object with no prototype
26
52
  */
27
53
  export function parseCookies(cookie) {
28
- const result = {};
29
- if (!cookie)
54
+ const result = Object.create(null);
55
+ if (typeof cookie !== "string")
30
56
  return result;
57
+ if (cookie.length === 0 || cookie.length > MAX_COOKIE_HEADER_LENGTH) {
58
+ return result;
59
+ }
60
+ let pairs = 0;
31
61
  for (const part of cookie.split(";")) {
62
+ if (pairs >= MAX_COOKIE_PAIRS)
63
+ break;
32
64
  const [key, ...rest] = part.trim().split("=");
33
65
  if (key && rest.length > 0) {
34
66
  result[key.trim()] = rest.join("=").trim();
67
+ pairs++;
35
68
  }
36
69
  }
37
70
  return result;
38
71
  }
39
72
  /**
40
- * Check if a token is expired without verifying the signature.
73
+ * Check if a token is expired **without verifying the signature**.
74
+ *
75
+ * The payload is attacker-controlled: treat the answer as a hint (e.g. "should
76
+ * I refresh before calling?"), never as an authorization decision.
41
77
  *
42
78
  * @param token - JWT token string
43
- * @returns Whether the token appears expired
79
+ * @returns Whether the token appears expired. Unparseable, oversized, or
80
+ * malformed input is reported as expired.
44
81
  */
45
82
  export function isTokenExpired(token) {
46
- try {
47
- const parts = token.split(".");
48
- if (parts.length !== 3)
49
- return true;
50
- const payload = JSON.parse(Buffer.from(parts[1].replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf-8"));
51
- const now = Math.floor(Date.now() / 1000);
52
- return typeof payload.exp !== "number" || payload.exp < now;
53
- }
54
- catch {
83
+ const parts = splitToken(token);
84
+ if (!parts)
55
85
  return true;
56
- }
86
+ const payload = decodeJsonSegment(parts[1]);
87
+ if (!payload)
88
+ return true;
89
+ const exp = payload["exp"];
90
+ const now = Math.floor(Date.now() / 1000);
91
+ return typeof exp !== "number" || !Number.isFinite(exp) || exp < now;
57
92
  }
58
93
  /**
59
- * Extract the user ID from a JWT payload without verifying.
94
+ * Extract the user ID from a JWT payload **without verifying the signature**.
95
+ *
96
+ * The returned value is attacker-controlled. It is safe to use as a
97
+ * diagnostic hint; it must never be used to decide who the caller is, or as
98
+ * a key in anything security-relevant, without verifying the token first.
60
99
  *
61
100
  * @param token - JWT token string
62
- * @returns User ID or null
101
+ * @returns The `sub` claim when it is a non-empty string, otherwise null.
102
+ * A `sub` that is a number, object, or array is reported as null rather
103
+ * than leaking a non-string through a `string | null` signature.
63
104
  */
64
105
  export function extractUserId(token) {
65
- try {
66
- const parts = token.split(".");
67
- if (parts.length !== 3)
68
- return null;
69
- const payload = JSON.parse(Buffer.from(parts[1].replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf-8"));
70
- return payload.sub ?? null;
71
- }
72
- catch {
106
+ const parts = splitToken(token);
107
+ if (!parts)
73
108
  return null;
74
- }
109
+ const payload = decodeJsonSegment(parts[1]);
110
+ if (!payload)
111
+ return null;
112
+ const sub = payload["sub"];
113
+ return typeof sub === "string" && sub.length > 0 ? sub : null;
75
114
  }
76
115
  /**
77
116
  * Generate a CSRF token.
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@zudojs/auth",
3
- "version": "0.1.1",
4
- "description": "Authentication and authorization services for the Zudojs framework — JWT, sessions, RBAC, OAuth2, and password management.",
3
+ "version": "1.1.0",
4
+ "description": "Authentication and authorization services for the Zudojs framework — JWT, sessions, RBAC, and password management.",
5
5
  "license": "MIT",
6
+ "author": {
7
+ "name": "Oluwayemi Oyinlola",
8
+ "url": "https://github.com/oyinlola-tech"
9
+ },
6
10
  "type": "module",
7
11
  "main": "./dist/index.js",
8
12
  "module": "./dist/index.js",
@@ -15,25 +19,21 @@
15
19
  }
16
20
  },
17
21
  "files": [
18
- "dist"
22
+ "dist",
23
+ "!dist/**/*.map",
24
+ "!dist/**/*.tsbuildinfo",
25
+ "!dist/.tsbuildinfo"
19
26
  ],
20
- "scripts": {
21
- "build": "tsc -p tsconfig.json",
22
- "typecheck": "tsc -p tsconfig.json --noEmit",
23
- "clean": "rm -rf dist",
24
- "test": "vitest run",
25
- "test:watch": "vitest"
26
- },
27
27
  "dependencies": {
28
- "@zudojs/errors": "0.1.0",
29
- "@zudojs/constants": "0.1.0",
30
- "@zudojs/permissions": "0.1.0"
28
+ "@zudojs/errors": "1.0.1",
29
+ "@zudojs/constants": "1.0.1",
30
+ "@zudojs/permissions": "1.1.0"
31
31
  },
32
32
  "engines": {
33
33
  "node": ">=24.0.0"
34
34
  },
35
35
  "devDependencies": {
36
- "typescript": "^7.0.2",
36
+ "typescript": "7.0.2",
37
37
  "vitest": "^4.1.11"
38
38
  },
39
39
  "publishConfig": {
@@ -48,8 +48,19 @@
48
48
  "rbac"
49
49
  ],
50
50
  "homepage": "https://github.com/oyinlola-tech/zudo#readme",
51
+ "bugs": {
52
+ "url": "https://github.com/oyinlola-tech/zudo/issues"
53
+ },
51
54
  "repository": {
52
55
  "type": "git",
53
- "url": "https://github.com/oyinlola-tech/zudo"
56
+ "url": "https://github.com/oyinlola-tech/zudo",
57
+ "directory": "packages/auth"
58
+ },
59
+ "scripts": {
60
+ "build": "tsc -p tsconfig.json",
61
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
62
+ "clean": "rm -rf dist",
63
+ "test": "vitest run",
64
+ "test:watch": "vitest"
54
65
  }
55
- }
66
+ }