@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.
- package/LICENSE +21 -0
- package/README.md +224 -19
- package/dist/authErrors/authError.base.d.ts +50 -16
- package/dist/authErrors/authError.base.js +94 -18
- package/dist/authErrors/index.d.ts +1 -1
- package/dist/authErrors/index.js +1 -1
- package/dist/authPassword/authPassword.core.d.ts +25 -2
- package/dist/authPassword/authPassword.core.js +50 -2
- package/dist/authPassword/index.d.ts +1 -1
- package/dist/authPassword/index.js +1 -1
- package/dist/authProvider/authAttempt.memory.d.ts +21 -0
- package/dist/authProvider/authAttempt.memory.js +91 -0
- package/dist/authProvider/authProvider.core.d.ts +63 -9
- package/dist/authProvider/authProvider.core.js +221 -37
- package/dist/authProvider/index.d.ts +2 -1
- package/dist/authProvider/index.js +1 -0
- package/dist/authSession/authSession.core.d.ts +20 -3
- package/dist/authSession/authSession.core.js +71 -21
- package/dist/authToken/authToken.core.d.ts +27 -1
- package/dist/authToken/authToken.core.js +31 -1
- package/dist/authToken/authToken.encoding.d.ts +43 -0
- package/dist/authToken/authToken.encoding.js +74 -0
- package/dist/authToken/authToken.revocation.d.ts +10 -2
- package/dist/authToken/authToken.revocation.js +42 -7
- package/dist/authToken/authToken.signing.d.ts +24 -1
- package/dist/authToken/authToken.signing.js +117 -28
- package/dist/authToken/jwt.namespace.d.ts +18 -8
- package/dist/authToken/jwt.namespace.js +11 -1
- package/dist/authTypes/authAttempt.type.d.ts +65 -0
- package/dist/authTypes/authAttempt.type.js +7 -0
- package/dist/authTypes/authCredentials.type.d.ts +26 -0
- package/dist/authTypes/authCredentials.type.js +11 -0
- package/dist/authTypes/authSession.type.d.ts +31 -3
- package/dist/authTypes/authSession.type.js +17 -1
- package/dist/authTypes/authToken.type.d.ts +29 -1
- package/dist/authTypes/authUser.type.d.ts +12 -0
- package/dist/authTypes/authUser.type.js +17 -1
- package/dist/authTypes/index.d.ts +5 -4
- package/dist/authTypes/index.js +5 -4
- package/dist/authUtils/authUtils.helper.d.ts +31 -10
- package/dist/authUtils/authUtils.helper.js +70 -31
- package/package.json +27 -16
- package/dist/.tsbuildinfo +0 -1
- package/dist/authErrors/authError.base.d.ts.map +0 -1
- package/dist/authErrors/authError.base.js.map +0 -1
- package/dist/authErrors/index.d.ts.map +0 -1
- package/dist/authErrors/index.js.map +0 -1
- package/dist/authPassword/authPassword.core.d.ts.map +0 -1
- package/dist/authPassword/authPassword.core.js.map +0 -1
- package/dist/authPassword/index.d.ts.map +0 -1
- package/dist/authPassword/index.js.map +0 -1
- package/dist/authProvider/authProvider.core.d.ts.map +0 -1
- package/dist/authProvider/authProvider.core.js.map +0 -1
- package/dist/authProvider/index.d.ts.map +0 -1
- package/dist/authProvider/index.js.map +0 -1
- package/dist/authSession/authSession.core.d.ts.map +0 -1
- package/dist/authSession/authSession.core.js.map +0 -1
- package/dist/authSession/index.d.ts.map +0 -1
- package/dist/authSession/index.js.map +0 -1
- package/dist/authToken/authToken.core.d.ts.map +0 -1
- package/dist/authToken/authToken.core.js.map +0 -1
- package/dist/authToken/authToken.revocation.d.ts.map +0 -1
- package/dist/authToken/authToken.revocation.js.map +0 -1
- package/dist/authToken/authToken.signing.d.ts.map +0 -1
- package/dist/authToken/authToken.signing.js.map +0 -1
- package/dist/authToken/index.d.ts.map +0 -1
- package/dist/authToken/index.js.map +0 -1
- package/dist/authToken/jwt.namespace.d.ts.map +0 -1
- package/dist/authToken/jwt.namespace.js.map +0 -1
- package/dist/authTypes/authRbac.type.d.ts.map +0 -1
- package/dist/authTypes/authRbac.type.js.map +0 -1
- package/dist/authTypes/authSession.type.d.ts.map +0 -1
- package/dist/authTypes/authSession.type.js.map +0 -1
- package/dist/authTypes/authStrategy.type.d.ts +0 -83
- package/dist/authTypes/authStrategy.type.d.ts.map +0 -1
- package/dist/authTypes/authStrategy.type.js +0 -7
- package/dist/authTypes/authStrategy.type.js.map +0 -1
- package/dist/authTypes/authToken.type.d.ts.map +0 -1
- package/dist/authTypes/authToken.type.js.map +0 -1
- package/dist/authTypes/authUser.type.d.ts.map +0 -1
- package/dist/authTypes/authUser.type.js.map +0 -1
- package/dist/authTypes/index.d.ts.map +0 -1
- package/dist/authTypes/index.js.map +0 -1
- package/dist/authUtils/authUtils.helper.d.ts.map +0 -1
- package/dist/authUtils/authUtils.helper.js.map +0 -1
- package/dist/authUtils/index.d.ts.map +0 -1
- package/dist/authUtils/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- 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 (
|
|
34
|
+
if (typeof authorization !== "string")
|
|
15
35
|
return null;
|
|
16
|
-
|
|
17
|
-
if (parts.length !== 2 || parts[0] !== "Bearer")
|
|
36
|
+
if (authorization.length > MAX_AUTHORIZATION_LENGTH)
|
|
18
37
|
return null;
|
|
19
|
-
|
|
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
|
-
*
|
|
25
|
-
*
|
|
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 (
|
|
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
|
-
|
|
47
|
-
|
|
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
|
|
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
|
-
|
|
66
|
-
|
|
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": "
|
|
4
|
-
"description": "Authentication and authorization services for the Zudojs framework — JWT, sessions, RBAC,
|
|
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
|
|
29
|
-
"@zudojs/constants": "0.1
|
|
30
|
-
"@zudojs/permissions": "
|
|
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": "
|
|
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
|
+
}
|