@c9up/warden 0.1.3 → 0.1.5
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/README.md +57 -0
- package/dist/AuthManager.d.ts +98 -0
- package/dist/AuthManager.d.ts.map +1 -0
- package/dist/AuthManager.js +147 -0
- package/dist/AuthManager.js.map +1 -0
- package/dist/AuthRateLimiter.d.ts +29 -0
- package/dist/AuthRateLimiter.d.ts.map +1 -0
- package/dist/AuthRateLimiter.js +88 -0
- package/dist/AuthRateLimiter.js.map +1 -0
- package/dist/Guard.d.ts +35 -0
- package/dist/Guard.d.ts.map +1 -0
- package/dist/Guard.js +68 -0
- package/dist/Guard.js.map +1 -0
- package/dist/RedisBlacklistDriver.d.ts +32 -0
- package/dist/RedisBlacklistDriver.d.ts.map +1 -0
- package/dist/RedisBlacklistDriver.js +37 -0
- package/dist/RedisBlacklistDriver.js.map +1 -0
- package/dist/RefreshTokenStore.d.ts +34 -0
- package/dist/RefreshTokenStore.d.ts.map +1 -0
- package/dist/RefreshTokenStore.js +48 -0
- package/dist/RefreshTokenStore.js.map +1 -0
- package/dist/ResilientBlacklistDriver.d.ts +35 -0
- package/dist/ResilientBlacklistDriver.d.ts.map +1 -0
- package/dist/ResilientBlacklistDriver.js +61 -0
- package/dist/ResilientBlacklistDriver.js.map +1 -0
- package/dist/TokenBlacklist.d.ts +30 -0
- package/dist/TokenBlacklist.d.ts.map +1 -0
- package/dist/TokenBlacklist.js +50 -0
- package/dist/TokenBlacklist.js.map +1 -0
- package/dist/WardenProvider.d.ts +19 -0
- package/dist/WardenProvider.d.ts.map +1 -0
- package/dist/WardenProvider.js +71 -0
- package/dist/WardenProvider.js.map +1 -0
- package/dist/bouncer/AuthorizationResponse.d.ts +25 -0
- package/dist/bouncer/AuthorizationResponse.d.ts.map +1 -0
- package/dist/bouncer/AuthorizationResponse.js +31 -0
- package/dist/bouncer/AuthorizationResponse.js.map +1 -0
- package/dist/bouncer/BasePolicy.d.ts +47 -0
- package/dist/bouncer/BasePolicy.d.ts.map +1 -0
- package/dist/bouncer/BasePolicy.js +36 -0
- package/dist/bouncer/BasePolicy.js.map +1 -0
- package/dist/bouncer/Bouncer.d.ts +47 -0
- package/dist/bouncer/Bouncer.d.ts.map +1 -0
- package/dist/bouncer/Bouncer.js +134 -0
- package/dist/bouncer/Bouncer.js.map +1 -0
- package/dist/bouncer/PolicyAuthorizer.d.ts +24 -0
- package/dist/bouncer/PolicyAuthorizer.d.ts.map +1 -0
- package/dist/bouncer/PolicyAuthorizer.js +92 -0
- package/dist/bouncer/PolicyAuthorizer.js.map +1 -0
- package/dist/bouncer/decorators.d.ts +25 -0
- package/dist/bouncer/decorators.d.ts.map +1 -0
- package/dist/bouncer/decorators.js +36 -0
- package/dist/bouncer/decorators.js.map +1 -0
- package/dist/bouncer/evaluate.d.ts +36 -0
- package/dist/bouncer/evaluate.d.ts.map +1 -0
- package/dist/bouncer/evaluate.js +64 -0
- package/dist/bouncer/evaluate.js.map +1 -0
- package/dist/bouncer/policyContext.d.ts +29 -0
- package/dist/bouncer/policyContext.d.ts.map +1 -0
- package/dist/bouncer/policyContext.js +36 -0
- package/dist/bouncer/policyContext.js.map +1 -0
- package/dist/bouncer/types.d.ts +52 -0
- package/dist/bouncer/types.d.ts.map +1 -0
- package/dist/bouncer/types.js +9 -0
- package/dist/bouncer/types.js.map +1 -0
- package/dist/config.d.ts +51 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +21 -0
- package/dist/config.js.map +1 -0
- package/dist/configure.d.ts +10 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +44 -0
- package/dist/configure.js.map +1 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -0
- package/dist/firstcontact/FirstContactManager.d.ts +25 -0
- package/dist/firstcontact/FirstContactManager.d.ts.map +1 -0
- package/dist/firstcontact/FirstContactManager.js +38 -0
- package/dist/firstcontact/FirstContactManager.js.map +1 -0
- package/dist/firstcontact/drivers/GitHubDriver.d.ts +14 -0
- package/dist/firstcontact/drivers/GitHubDriver.d.ts.map +1 -0
- package/dist/firstcontact/drivers/GitHubDriver.js +61 -0
- package/dist/firstcontact/drivers/GitHubDriver.js.map +1 -0
- package/dist/firstcontact/drivers/GoogleDriver.d.ts +14 -0
- package/dist/firstcontact/drivers/GoogleDriver.d.ts.map +1 -0
- package/dist/firstcontact/drivers/GoogleDriver.js +64 -0
- package/dist/firstcontact/drivers/GoogleDriver.js.map +1 -0
- package/dist/firstcontact/types.d.ts +34 -0
- package/dist/firstcontact/types.d.ts.map +1 -0
- package/dist/firstcontact/types.js +5 -0
- package/dist/firstcontact/types.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/mfa/BackupCodesProvider.d.ts +39 -0
- package/dist/mfa/BackupCodesProvider.d.ts.map +1 -0
- package/dist/mfa/BackupCodesProvider.js +87 -0
- package/dist/mfa/BackupCodesProvider.js.map +1 -0
- package/dist/mfa/MfaManager.d.ts +107 -0
- package/dist/mfa/MfaManager.d.ts.map +1 -0
- package/dist/mfa/MfaManager.js +219 -0
- package/dist/mfa/MfaManager.js.map +1 -0
- package/dist/mfa/OtpProvider.d.ts +67 -0
- package/dist/mfa/OtpProvider.d.ts.map +1 -0
- package/dist/mfa/OtpProvider.js +104 -0
- package/dist/mfa/OtpProvider.js.map +1 -0
- package/dist/mfa/TotpProvider.d.ts +52 -0
- package/dist/mfa/TotpProvider.d.ts.map +1 -0
- package/dist/mfa/TotpProvider.js +103 -0
- package/dist/mfa/TotpProvider.js.map +1 -0
- package/dist/mfa/WebauthnProvider.d.ts +175 -0
- package/dist/mfa/WebauthnProvider.d.ts.map +1 -0
- package/dist/mfa/WebauthnProvider.js +239 -0
- package/dist/mfa/WebauthnProvider.js.map +1 -0
- package/dist/mfa/base32.d.ts +13 -0
- package/dist/mfa/base32.d.ts.map +1 -0
- package/dist/mfa/base32.js +52 -0
- package/dist/mfa/base32.js.map +1 -0
- package/dist/mfa/webauthn-codec.d.ts +58 -0
- package/dist/mfa/webauthn-codec.d.ts.map +1 -0
- package/dist/mfa/webauthn-codec.js +221 -0
- package/dist/mfa/webauthn-codec.js.map +1 -0
- package/dist/middleware.d.ts +51 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +213 -0
- package/dist/middleware.js.map +1 -0
- package/dist/native.d.ts +14 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +39 -0
- package/dist/native.js.map +1 -0
- package/dist/rights/MemoryRightsStore.d.ts +29 -0
- package/dist/rights/MemoryRightsStore.d.ts.map +1 -0
- package/dist/rights/MemoryRightsStore.js +70 -0
- package/dist/rights/MemoryRightsStore.js.map +1 -0
- package/dist/rights/RightsResolver.d.ts +29 -0
- package/dist/rights/RightsResolver.d.ts.map +1 -0
- package/dist/rights/RightsResolver.js +73 -0
- package/dist/rights/RightsResolver.js.map +1 -0
- package/dist/rights/types.d.ts +55 -0
- package/dist/rights/types.d.ts.map +1 -0
- package/dist/rights/types.js +18 -0
- package/dist/rights/types.js.map +1 -0
- package/dist/services/main.d.ts +19 -0
- package/dist/services/main.d.ts.map +1 -0
- package/dist/services/main.js +32 -0
- package/dist/services/main.js.map +1 -0
- package/dist/standalone.d.ts +37 -0
- package/dist/standalone.d.ts.map +1 -0
- package/dist/standalone.js +92 -0
- package/dist/standalone.js.map +1 -0
- package/dist/strategies/ApiKeyStrategy.d.ts +28 -0
- package/dist/strategies/ApiKeyStrategy.d.ts.map +1 -0
- package/dist/strategies/ApiKeyStrategy.js +36 -0
- package/dist/strategies/ApiKeyStrategy.js.map +1 -0
- package/dist/strategies/JwtStrategy.d.ts +51 -0
- package/dist/strategies/JwtStrategy.d.ts.map +1 -0
- package/dist/strategies/JwtStrategy.js +150 -0
- package/dist/strategies/JwtStrategy.js.map +1 -0
- package/dist/strategies/SessionStrategy.d.ts +58 -0
- package/dist/strategies/SessionStrategy.d.ts.map +1 -0
- package/dist/strategies/SessionStrategy.js +70 -0
- package/dist/strategies/SessionStrategy.js.map +1 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
- package/src/Guard.ts +21 -0
- package/src/RedisBlacklistDriver.ts +60 -0
- package/src/ResilientBlacklistDriver.ts +84 -0
- package/src/WardenProvider.ts +11 -0
- package/src/config.ts +7 -0
- package/src/index.ts +58 -0
- package/src/mfa/BackupCodesProvider.ts +125 -0
- package/src/mfa/MfaManager.ts +307 -0
- package/src/mfa/OtpProvider.ts +177 -0
- package/src/mfa/TotpProvider.ts +140 -0
- package/src/mfa/WebauthnProvider.ts +416 -0
- package/src/mfa/base32.ts +54 -0
- package/src/mfa/webauthn-codec.ts +264 -0
- package/src/middleware.ts +14 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 4648 base32 (no padding) — the encoding authenticator apps expect for
|
|
3
|
+
* TOTP secrets. Kept dependency-free and local to the MFA subsystem.
|
|
4
|
+
*/
|
|
5
|
+
const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
6
|
+
const CHAR_TO_VAL = {};
|
|
7
|
+
for (let i = 0; i < ALPHABET.length; i++) {
|
|
8
|
+
CHAR_TO_VAL[ALPHABET[i]] = i;
|
|
9
|
+
}
|
|
10
|
+
/** Encode bytes to an unpadded, uppercase base32 string. */
|
|
11
|
+
export function base32Encode(bytes) {
|
|
12
|
+
let bits = 0;
|
|
13
|
+
let value = 0;
|
|
14
|
+
let out = "";
|
|
15
|
+
for (const byte of bytes) {
|
|
16
|
+
value = (value << 8) | byte;
|
|
17
|
+
bits += 8;
|
|
18
|
+
while (bits >= 5) {
|
|
19
|
+
out += ALPHABET[(value >>> (bits - 5)) & 31];
|
|
20
|
+
bits -= 5;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (bits > 0) {
|
|
24
|
+
out += ALPHABET[(value << (5 - bits)) & 31];
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Decode a base32 string to bytes. Tolerant of lowercase, spaces, and `=`
|
|
30
|
+
* padding (authenticator apps and manual entry produce all three). Throws on
|
|
31
|
+
* any character outside the alphabet.
|
|
32
|
+
*/
|
|
33
|
+
export function base32Decode(input) {
|
|
34
|
+
const clean = input.replace(/[\s=]/g, "").toUpperCase();
|
|
35
|
+
let bits = 0;
|
|
36
|
+
let value = 0;
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const ch of clean) {
|
|
39
|
+
const v = CHAR_TO_VAL[ch];
|
|
40
|
+
if (v === undefined) {
|
|
41
|
+
throw new Error(`Invalid base32 character: ${ch}`);
|
|
42
|
+
}
|
|
43
|
+
value = (value << 5) | v;
|
|
44
|
+
bits += 5;
|
|
45
|
+
if (bits >= 8) {
|
|
46
|
+
out.push((value >>> (bits - 8)) & 0xff);
|
|
47
|
+
bits -= 8;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return Uint8Array.from(out);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=base32.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base32.js","sourceRoot":"","sources":["../../src/mfa/base32.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,QAAQ,GAAG,kCAAkC,CAAC;AACpD,MAAM,WAAW,GAA2B,EAAE,CAAC;AAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;IAC1C,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,YAAY,CAAC,KAAiB;IAC7C,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,KAAK,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QAC5B,IAAI,IAAI,CAAC,CAAC;QACV,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC;YAClB,GAAG,IAAI,QAAQ,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YAC7C,IAAI,IAAI,CAAC,CAAC;QACX,CAAC;IACF,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACd,GAAG,IAAI,QAAQ,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACzC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACxD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,6BAA6B,EAAE,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,KAAK,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,IAAI,CAAC,CAAC;QACV,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;YACf,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;YACxC,IAAI,IAAI,CAAC,CAAC;QACX,CAAC;IACF,CAAC;IACD,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebAuthn wire-format codec — the parsing and crypto primitives behind
|
|
3
|
+
* `WebauthnProvider`, implemented from scratch on `node:crypto` so warden
|
|
4
|
+
* carries no third-party dependency.
|
|
5
|
+
*
|
|
6
|
+
* Scope: the subset of CBOR / COSE / authenticator-data that FIDO2 ceremonies
|
|
7
|
+
* actually use, with public-key algorithms Node can import via JWK:
|
|
8
|
+
* - ES256 / ES384 / ES512 (ECDSA, COSE alg -7 / -35 / -36)
|
|
9
|
+
* - RS256 / RS384 / RS512 (RSASSA-PKCS1-v1_5, alg -257 / -258 / -259)
|
|
10
|
+
* - EdDSA / Ed25519 (alg -8)
|
|
11
|
+
*
|
|
12
|
+
* Attestation *statements* are intentionally not verified (see WebauthnProvider).
|
|
13
|
+
*/
|
|
14
|
+
import { type KeyObject } from "node:crypto";
|
|
15
|
+
export type CborValue = number | string | Buffer | CborValue[] | CborMap | boolean | null;
|
|
16
|
+
export type CborMap = Map<number | string, CborValue>;
|
|
17
|
+
export declare function base64urlToBuffer(s: string): Buffer;
|
|
18
|
+
export declare function bufferToBase64url(b: Buffer | Uint8Array): string;
|
|
19
|
+
export declare function sha256(data: Buffer): Buffer;
|
|
20
|
+
interface Decoded {
|
|
21
|
+
value: CborValue;
|
|
22
|
+
/** Byte offset immediately after the decoded item. */
|
|
23
|
+
next: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Decode a single CBOR item starting at `start`. Definite-length only —
|
|
27
|
+
* indefinite-length items and floats are rejected (WebAuthn uses neither).
|
|
28
|
+
*/
|
|
29
|
+
export declare function decodeCbor(buf: Buffer, start?: number): Decoded;
|
|
30
|
+
export interface AuthenticatorData {
|
|
31
|
+
rpIdHash: Buffer;
|
|
32
|
+
flags: {
|
|
33
|
+
up: boolean;
|
|
34
|
+
uv: boolean;
|
|
35
|
+
at: boolean;
|
|
36
|
+
ed: boolean;
|
|
37
|
+
};
|
|
38
|
+
signCount: number;
|
|
39
|
+
credentialId?: Buffer;
|
|
40
|
+
cosePublicKey?: CborMap;
|
|
41
|
+
/** Raw COSE_Key bytes, ready to persist and re-decode at sign-in time. */
|
|
42
|
+
cosePublicKeyBytes?: Buffer;
|
|
43
|
+
}
|
|
44
|
+
/** Parse the raw authenticator-data structure (§6.1 of the WebAuthn spec). */
|
|
45
|
+
export declare function parseAuthenticatorData(authData: Buffer): AuthenticatorData;
|
|
46
|
+
/** Convert a COSE_Key map into a Node public key plus its COSE algorithm id. */
|
|
47
|
+
export declare function coseToKeyObject(cose: CborMap): {
|
|
48
|
+
key: KeyObject;
|
|
49
|
+
alg: number;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Verify a WebAuthn assertion signature over `data` (= authenticatorData ‖
|
|
53
|
+
* SHA-256(clientDataJSON)). ECDSA signatures are DER-encoded, matching what
|
|
54
|
+
* authenticators emit.
|
|
55
|
+
*/
|
|
56
|
+
export declare function verifyWebauthnSignature(alg: number, key: KeyObject, data: Buffer, signature: Buffer): boolean;
|
|
57
|
+
export {};
|
|
58
|
+
//# sourceMappingURL=webauthn-codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webauthn-codec.d.ts","sourceRoot":"","sources":["../../src/mfa/webauthn-codec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAKN,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,SAAS,GAClB,MAAM,GACN,MAAM,GACN,MAAM,GACN,SAAS,EAAE,GACX,OAAO,GACP,OAAO,GACP,IAAI,CAAC;AACR,MAAM,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE,SAAS,CAAC,CAAC;AAEtD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAEhE;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,UAAU,OAAO;IAChB,KAAK,EAAE,SAAS,CAAC;IACjB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,OAAO,CAwE1D;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,8EAA8E;AAC9E,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CA+B1E;AAmBD,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG;IAC/C,GAAG,EAAE,SAAS,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACZ,CAuCA;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACtC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GACf,OAAO,CAKT"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebAuthn wire-format codec — the parsing and crypto primitives behind
|
|
3
|
+
* `WebauthnProvider`, implemented from scratch on `node:crypto` so warden
|
|
4
|
+
* carries no third-party dependency.
|
|
5
|
+
*
|
|
6
|
+
* Scope: the subset of CBOR / COSE / authenticator-data that FIDO2 ceremonies
|
|
7
|
+
* actually use, with public-key algorithms Node can import via JWK:
|
|
8
|
+
* - ES256 / ES384 / ES512 (ECDSA, COSE alg -7 / -35 / -36)
|
|
9
|
+
* - RS256 / RS384 / RS512 (RSASSA-PKCS1-v1_5, alg -257 / -258 / -259)
|
|
10
|
+
* - EdDSA / Ed25519 (alg -8)
|
|
11
|
+
*
|
|
12
|
+
* Attestation *statements* are intentionally not verified (see WebauthnProvider).
|
|
13
|
+
*/
|
|
14
|
+
import { createHash, createPublicKey, verify as cryptoVerify, } from "node:crypto";
|
|
15
|
+
export function base64urlToBuffer(s) {
|
|
16
|
+
return Buffer.from(s, "base64url");
|
|
17
|
+
}
|
|
18
|
+
export function bufferToBase64url(b) {
|
|
19
|
+
return Buffer.from(b).toString("base64url");
|
|
20
|
+
}
|
|
21
|
+
export function sha256(data) {
|
|
22
|
+
return createHash("sha256").update(data).digest();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Decode a single CBOR item starting at `start`. Definite-length only —
|
|
26
|
+
* indefinite-length items and floats are rejected (WebAuthn uses neither).
|
|
27
|
+
*/
|
|
28
|
+
export function decodeCbor(buf, start = 0) {
|
|
29
|
+
const major = buf[start] >> 5;
|
|
30
|
+
const info = buf[start] & 0x1f;
|
|
31
|
+
let len;
|
|
32
|
+
let p = start + 1;
|
|
33
|
+
if (info < 24) {
|
|
34
|
+
len = info;
|
|
35
|
+
}
|
|
36
|
+
else if (info === 24) {
|
|
37
|
+
len = buf[p];
|
|
38
|
+
p += 1;
|
|
39
|
+
}
|
|
40
|
+
else if (info === 25) {
|
|
41
|
+
len = buf.readUInt16BE(p);
|
|
42
|
+
p += 2;
|
|
43
|
+
}
|
|
44
|
+
else if (info === 26) {
|
|
45
|
+
len = buf.readUInt32BE(p);
|
|
46
|
+
p += 4;
|
|
47
|
+
}
|
|
48
|
+
else if (info === 27) {
|
|
49
|
+
len = Number(buf.readBigUInt64BE(p));
|
|
50
|
+
p += 8;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
throw new Error("CBOR: indefinite or reserved length is not supported");
|
|
54
|
+
}
|
|
55
|
+
switch (major) {
|
|
56
|
+
case 0: // unsigned int
|
|
57
|
+
return { value: len, next: p };
|
|
58
|
+
case 1: // negative int
|
|
59
|
+
return { value: -1 - len, next: p };
|
|
60
|
+
case 2: // byte string
|
|
61
|
+
return { value: buf.subarray(p, p + len), next: p + len };
|
|
62
|
+
case 3: // text string
|
|
63
|
+
return { value: buf.toString("utf8", p, p + len), next: p + len };
|
|
64
|
+
case 4: {
|
|
65
|
+
// array
|
|
66
|
+
const arr = [];
|
|
67
|
+
let cur = p;
|
|
68
|
+
for (let i = 0; i < len; i++) {
|
|
69
|
+
const d = decodeCbor(buf, cur);
|
|
70
|
+
arr.push(d.value);
|
|
71
|
+
cur = d.next;
|
|
72
|
+
}
|
|
73
|
+
return { value: arr, next: cur };
|
|
74
|
+
}
|
|
75
|
+
case 5: {
|
|
76
|
+
// map
|
|
77
|
+
const map = new Map();
|
|
78
|
+
let cur = p;
|
|
79
|
+
for (let i = 0; i < len; i++) {
|
|
80
|
+
const k = decodeCbor(buf, cur);
|
|
81
|
+
cur = k.next;
|
|
82
|
+
const v = decodeCbor(buf, cur);
|
|
83
|
+
cur = v.next;
|
|
84
|
+
if (typeof k.value !== "number" && typeof k.value !== "string") {
|
|
85
|
+
throw new Error("CBOR: only integer/text map keys are supported");
|
|
86
|
+
}
|
|
87
|
+
map.set(k.value, v.value);
|
|
88
|
+
}
|
|
89
|
+
return { value: map, next: cur };
|
|
90
|
+
}
|
|
91
|
+
case 6: {
|
|
92
|
+
// tag — decode and surface the tagged content
|
|
93
|
+
const d = decodeCbor(buf, p);
|
|
94
|
+
return { value: d.value, next: d.next };
|
|
95
|
+
}
|
|
96
|
+
case 7:
|
|
97
|
+
if (info === 20)
|
|
98
|
+
return { value: false, next: p };
|
|
99
|
+
if (info === 21)
|
|
100
|
+
return { value: true, next: p };
|
|
101
|
+
if (info === 22)
|
|
102
|
+
return { value: null, next: p };
|
|
103
|
+
throw new Error("CBOR: simple/float values are not supported");
|
|
104
|
+
default:
|
|
105
|
+
throw new Error(`CBOR: unknown major type ${major}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Parse the raw authenticator-data structure (§6.1 of the WebAuthn spec). */
|
|
109
|
+
export function parseAuthenticatorData(authData) {
|
|
110
|
+
if (authData.length < 37) {
|
|
111
|
+
throw new Error("authenticatorData is too short");
|
|
112
|
+
}
|
|
113
|
+
const flagsByte = authData[32];
|
|
114
|
+
const flags = {
|
|
115
|
+
up: (flagsByte & 0x01) !== 0,
|
|
116
|
+
uv: (flagsByte & 0x04) !== 0,
|
|
117
|
+
at: (flagsByte & 0x40) !== 0,
|
|
118
|
+
ed: (flagsByte & 0x80) !== 0,
|
|
119
|
+
};
|
|
120
|
+
const result = {
|
|
121
|
+
rpIdHash: authData.subarray(0, 32),
|
|
122
|
+
flags,
|
|
123
|
+
signCount: authData.readUInt32BE(33),
|
|
124
|
+
};
|
|
125
|
+
if (flags.at) {
|
|
126
|
+
if (authData.length < 55) {
|
|
127
|
+
throw new Error("attested credential data is truncated");
|
|
128
|
+
}
|
|
129
|
+
const credIdLen = authData.readUInt16BE(53);
|
|
130
|
+
result.credentialId = authData.subarray(55, 55 + credIdLen);
|
|
131
|
+
const coseStart = 55 + credIdLen;
|
|
132
|
+
const decoded = decodeCbor(authData, coseStart);
|
|
133
|
+
if (!(decoded.value instanceof Map)) {
|
|
134
|
+
throw new Error("COSE public key is not a CBOR map");
|
|
135
|
+
}
|
|
136
|
+
result.cosePublicKey = decoded.value;
|
|
137
|
+
result.cosePublicKeyBytes = authData.subarray(coseStart, decoded.next);
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
// COSE algorithm id → the hash Node should use ( `null` = pre-hashed / Ed25519 ).
|
|
142
|
+
const ALG_HASH = {
|
|
143
|
+
[-7]: "sha256",
|
|
144
|
+
[-35]: "sha384",
|
|
145
|
+
[-36]: "sha512",
|
|
146
|
+
[-257]: "sha256",
|
|
147
|
+
[-258]: "sha384",
|
|
148
|
+
[-259]: "sha512",
|
|
149
|
+
[-8]: null,
|
|
150
|
+
};
|
|
151
|
+
const EC_CURVES = {
|
|
152
|
+
1: "P-256",
|
|
153
|
+
2: "P-384",
|
|
154
|
+
3: "P-521",
|
|
155
|
+
};
|
|
156
|
+
/** Convert a COSE_Key map into a Node public key plus its COSE algorithm id. */
|
|
157
|
+
export function coseToKeyObject(cose) {
|
|
158
|
+
const kty = expectNumber(cose.get(1), "COSE kty");
|
|
159
|
+
const alg = expectNumber(cose.get(3), "COSE alg");
|
|
160
|
+
let jwk;
|
|
161
|
+
if (kty === 2) {
|
|
162
|
+
// EC2
|
|
163
|
+
const curve = EC_CURVES[expectNumber(cose.get(-1), "EC curve")];
|
|
164
|
+
if (!curve) {
|
|
165
|
+
throw new Error("unsupported COSE EC curve");
|
|
166
|
+
}
|
|
167
|
+
jwk = {
|
|
168
|
+
kty: "EC",
|
|
169
|
+
crv: curve,
|
|
170
|
+
x: bufferToBase64url(expectBytes(cose.get(-2), "EC x")),
|
|
171
|
+
y: bufferToBase64url(expectBytes(cose.get(-3), "EC y")),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
else if (kty === 3) {
|
|
175
|
+
// RSA
|
|
176
|
+
jwk = {
|
|
177
|
+
kty: "RSA",
|
|
178
|
+
n: bufferToBase64url(expectBytes(cose.get(-1), "RSA n")),
|
|
179
|
+
e: bufferToBase64url(expectBytes(cose.get(-2), "RSA e")),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
else if (kty === 1) {
|
|
183
|
+
// OKP / Ed25519
|
|
184
|
+
if (expectNumber(cose.get(-1), "OKP curve") !== 6) {
|
|
185
|
+
throw new Error("unsupported COSE OKP curve (only Ed25519)");
|
|
186
|
+
}
|
|
187
|
+
jwk = {
|
|
188
|
+
kty: "OKP",
|
|
189
|
+
crv: "Ed25519",
|
|
190
|
+
x: bufferToBase64url(expectBytes(cose.get(-2), "OKP x")),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
throw new Error(`unsupported COSE key type ${kty}`);
|
|
195
|
+
}
|
|
196
|
+
return { key: createPublicKey({ key: jwk, format: "jwk" }), alg };
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Verify a WebAuthn assertion signature over `data` (= authenticatorData ‖
|
|
200
|
+
* SHA-256(clientDataJSON)). ECDSA signatures are DER-encoded, matching what
|
|
201
|
+
* authenticators emit.
|
|
202
|
+
*/
|
|
203
|
+
export function verifyWebauthnSignature(alg, key, data, signature) {
|
|
204
|
+
if (!(alg in ALG_HASH)) {
|
|
205
|
+
throw new Error(`unsupported COSE algorithm ${alg}`);
|
|
206
|
+
}
|
|
207
|
+
return cryptoVerify(ALG_HASH[alg], data, key, signature);
|
|
208
|
+
}
|
|
209
|
+
function expectNumber(v, what) {
|
|
210
|
+
if (typeof v !== "number") {
|
|
211
|
+
throw new Error(`${what}: expected an integer`);
|
|
212
|
+
}
|
|
213
|
+
return v;
|
|
214
|
+
}
|
|
215
|
+
function expectBytes(v, what) {
|
|
216
|
+
if (!Buffer.isBuffer(v)) {
|
|
217
|
+
throw new Error(`${what}: expected a byte string`);
|
|
218
|
+
}
|
|
219
|
+
return v;
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=webauthn-codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webauthn-codec.js","sourceRoot":"","sources":["../../src/mfa/webauthn-codec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Warden auth middleware — resolves the AuthManager from the Ream IoC container
|
|
3
|
+
* and authenticates the request using the configured strategy.
|
|
4
|
+
*
|
|
5
|
+
* Same pattern as `@c9up/blackhole/middleware` and AdonisJS `@adonisjs/auth`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // start/kernel.ts
|
|
9
|
+
* router.use([() => import('@c9up/warden/middleware')])
|
|
10
|
+
*
|
|
11
|
+
* // On protected routes
|
|
12
|
+
* import { Guard } from '@c9up/warden'
|
|
13
|
+
* class OrderController {
|
|
14
|
+
* @Guard('jwt')
|
|
15
|
+
* async index(ctx) { ... }
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
import { type AuthResult } from "./AuthManager.js";
|
|
19
|
+
import type { SessionStore } from "./strategies/SessionStrategy.js";
|
|
20
|
+
export interface WardenContext {
|
|
21
|
+
request: {
|
|
22
|
+
method: string;
|
|
23
|
+
url: string;
|
|
24
|
+
headers: Record<string, string>;
|
|
25
|
+
};
|
|
26
|
+
response: {
|
|
27
|
+
status: (code: number) => void;
|
|
28
|
+
json: (data: unknown) => void;
|
|
29
|
+
};
|
|
30
|
+
container: {
|
|
31
|
+
resolve: (key: string | symbol | (abstract new (...args: never[]) => unknown)) => unknown;
|
|
32
|
+
};
|
|
33
|
+
/** Session store — set by a session middleware upstream. */
|
|
34
|
+
session?: SessionStore;
|
|
35
|
+
/** Set by the middleware after successful auth. */
|
|
36
|
+
auth?: AuthResult;
|
|
37
|
+
/** The route handler metadata (decorators). */
|
|
38
|
+
route?: {
|
|
39
|
+
controller?: object;
|
|
40
|
+
action?: string | symbol;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
type WardenNext = () => Promise<void> | void;
|
|
44
|
+
/**
|
|
45
|
+
* Ream auth middleware — resolves AuthManager from the container,
|
|
46
|
+
* reads `@Guard`/`@Permission`/`@Role` metadata from the route handler,
|
|
47
|
+
* and authenticates + authorizes the request.
|
|
48
|
+
*/
|
|
49
|
+
export declare function wardenMiddleware(ctx: WardenContext, next: WardenNext): Promise<void>;
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAEN,KAAK,UAAU,EAGf,MAAM,kBAAkB,CAAC;AAO1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAcpE,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE;QACR,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAChC,CAAC;IACF,QAAQ,EAAE;QACT,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAC/B,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;KAC9B,CAAC;IACF,SAAS,EAAE;QACV,OAAO,EAAE,CACR,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,KAC/D,OAAO,CAAC;KACb,CAAC;IACF,4DAA4D;IAC5D,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,mDAAmD;IACnD,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,+CAA+C;IAC/C,KAAK,CAAC,EAAE;QACP,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;KACzB,CAAC;CACF;AAED,KAAK,UAAU,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAE7C;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,iBAiH1E"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Warden auth middleware — resolves the AuthManager from the Ream IoC container
|
|
3
|
+
* and authenticates the request using the configured strategy.
|
|
4
|
+
*
|
|
5
|
+
* Same pattern as `@c9up/blackhole/middleware` and AdonisJS `@adonisjs/auth`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // start/kernel.ts
|
|
9
|
+
* router.use([() => import('@c9up/warden/middleware')])
|
|
10
|
+
*
|
|
11
|
+
* // On protected routes
|
|
12
|
+
* import { Guard } from '@c9up/warden'
|
|
13
|
+
* class OrderController {
|
|
14
|
+
* @Guard('jwt')
|
|
15
|
+
* async index(ctx) { ... }
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
import { AuthManager, sanitizePayload, } from "./AuthManager.js";
|
|
19
|
+
import { getGuardMetadata, getPermissionMetadata, getRequireMfaMetadata, getRoleMetadata, } from "./Guard.js";
|
|
20
|
+
function hasVerifyWithContext(strategy) {
|
|
21
|
+
return (typeof strategy.verifyWithContext === "function");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Ream auth middleware — resolves AuthManager from the container,
|
|
25
|
+
* reads `@Guard`/`@Permission`/`@Role` metadata from the route handler,
|
|
26
|
+
* and authenticates + authorizes the request.
|
|
27
|
+
*/
|
|
28
|
+
export async function wardenMiddleware(ctx, next) {
|
|
29
|
+
const auth = ctx.container.resolve(AuthManager);
|
|
30
|
+
// Read guard metadata from the route handler (if declared via decorators).
|
|
31
|
+
const controller = ctx.route?.controller;
|
|
32
|
+
const action = ctx.route?.action;
|
|
33
|
+
const strategies = controller && action ? getGuardMetadata(controller, action) : [];
|
|
34
|
+
// No guard on this route — pass through (public endpoint).
|
|
35
|
+
if (strategies.length === 0) {
|
|
36
|
+
await next();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Extract credentials from multiple sources:
|
|
40
|
+
// 1. Authorization: Bearer <jwt> (JWT strategy)
|
|
41
|
+
// 2. API key header — reads the header name from the ApiKeyStrategy config
|
|
42
|
+
// (defaults to 'x-api-key' if no ApiKeyStrategy is registered)
|
|
43
|
+
// 3. Cookie/session (Session strategy — handled by the strategy itself via context)
|
|
44
|
+
const authHeader = ctx.request.headers.authorization ?? "";
|
|
45
|
+
const bearerToken = authHeader.startsWith("Bearer ")
|
|
46
|
+
? authHeader.slice(7)
|
|
47
|
+
: "";
|
|
48
|
+
const apiKeyHeader = (() => {
|
|
49
|
+
try {
|
|
50
|
+
const s = auth.getStrategy("api-key");
|
|
51
|
+
return s.headerName ?? "x-api-key";
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return "x-api-key";
|
|
55
|
+
}
|
|
56
|
+
})();
|
|
57
|
+
// HTTP header names are case-insensitive. Node + every Ream-side
|
|
58
|
+
// runtime lowercases incoming header keys, so an `ApiKeyStrategy`
|
|
59
|
+
// declared with `headerName: "X-Custom-Key"` would never match the
|
|
60
|
+
// incoming `x-custom-key` key without this normalisation — auth
|
|
61
|
+
// would silently fail even with the right header on the wire.
|
|
62
|
+
const apiKey = ctx.request.headers[apiKeyHeader.toLowerCase()] ?? "";
|
|
63
|
+
const hasSessionStrategy = strategies.some((s) => s === "session");
|
|
64
|
+
if (!bearerToken && !apiKey && !hasSessionStrategy) {
|
|
65
|
+
ctx.response.status(401);
|
|
66
|
+
ctx.response.json({
|
|
67
|
+
error: {
|
|
68
|
+
code: "UNAUTHORIZED",
|
|
69
|
+
message: "Missing authentication token (Bearer or x-api-key)",
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Try each declared strategy. If every attempted strategy crashed AND none
|
|
75
|
+
// succeeded, the caller returns 500 instead of 401 — a server-side incident
|
|
76
|
+
// must stay observably distinct from a credential rejection.
|
|
77
|
+
const { result, attemptCount, crashCount } = await tryAuthenticate(auth, strategies, { bearerToken, apiKey, session: ctx.session, hasSessionStrategy });
|
|
78
|
+
if (!result?.authenticated || !result.user) {
|
|
79
|
+
if (attemptCount > 0 && crashCount === attemptCount) {
|
|
80
|
+
ctx.response.status(500);
|
|
81
|
+
ctx.response.json({
|
|
82
|
+
error: {
|
|
83
|
+
code: "AUTH_STRATEGY_ERROR",
|
|
84
|
+
message: "Authentication unavailable — one or more strategies failed. Check server logs.",
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
ctx.response.status(401);
|
|
90
|
+
ctx.response.json({
|
|
91
|
+
error: {
|
|
92
|
+
code: "UNAUTHORIZED",
|
|
93
|
+
message: result?.error ?? "Authentication failed",
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const user = result.user;
|
|
99
|
+
// Permission + role checks are independent AND gates: the user must satisfy
|
|
100
|
+
// ALL required permissions AND ALL required roles. Having a role does NOT
|
|
101
|
+
// bypass permission checks. This is the strictest model — callers who want
|
|
102
|
+
// role-OR-permission semantics should use a custom guard instead.
|
|
103
|
+
if (controller && action) {
|
|
104
|
+
const denied = await checkAuthorization(auth, user, controller, action);
|
|
105
|
+
if (denied) {
|
|
106
|
+
ctx.response.status(403);
|
|
107
|
+
ctx.response.json({ error: { code: "FORBIDDEN", message: denied } });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// @RequireMfa gate: the user must carry a truthy `mfa` claim, set by the
|
|
111
|
+
// app's step-up flow once MfaManager.verify() succeeds.
|
|
112
|
+
if (getRequireMfaMetadata(controller, action) && user.mfa !== true) {
|
|
113
|
+
ctx.response.status(403);
|
|
114
|
+
ctx.response.json({
|
|
115
|
+
error: {
|
|
116
|
+
code: "MFA_REQUIRED",
|
|
117
|
+
message: "Multi-factor authentication is required for this action.",
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Auth successful — attach to context and continue.
|
|
124
|
+
ctx.auth = result;
|
|
125
|
+
await next();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Try each declared strategy in order — session strategies via
|
|
129
|
+
* `verifyWithContext()`, others via `verify(token)` with native-first credential
|
|
130
|
+
* fallback. Distinguishes crashes (strategy threw / `strategyCrash`) from
|
|
131
|
+
* credential rejections so the caller can return 500 vs 401.
|
|
132
|
+
*/
|
|
133
|
+
async function tryAuthenticate(auth, strategies, creds) {
|
|
134
|
+
const { bearerToken, apiKey, session } = creds;
|
|
135
|
+
let result = null;
|
|
136
|
+
let attemptCount = 0;
|
|
137
|
+
let crashCount = 0;
|
|
138
|
+
for (const strategyName of strategies) {
|
|
139
|
+
try {
|
|
140
|
+
let r;
|
|
141
|
+
if (strategyName === "session") {
|
|
142
|
+
const strategy = auth.getStrategy(strategyName);
|
|
143
|
+
const verifyWithContext = strategy && hasVerifyWithContext(strategy)
|
|
144
|
+
? strategy.verifyWithContext
|
|
145
|
+
: undefined;
|
|
146
|
+
if (verifyWithContext) {
|
|
147
|
+
attemptCount++;
|
|
148
|
+
r = await verifyWithContext.call(strategy, "", { session });
|
|
149
|
+
// The session path bypasses AuthManager.verify(), so apply the
|
|
150
|
+
// same prototype-pollution guard JWT / api-key users get there.
|
|
151
|
+
if (r.user)
|
|
152
|
+
sanitizePayload(r.user);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
// Native-first credential, other transport as fallback so a
|
|
160
|
+
// single-credential client still authenticates (and an invalid
|
|
161
|
+
// Bearer no longer masks a valid API key for the api-key strategy).
|
|
162
|
+
const credential = strategyName === "api-key"
|
|
163
|
+
? apiKey || bearerToken
|
|
164
|
+
: bearerToken || apiKey;
|
|
165
|
+
if (!credential)
|
|
166
|
+
continue;
|
|
167
|
+
attemptCount++;
|
|
168
|
+
r = await auth.verify(credential, strategyName);
|
|
169
|
+
}
|
|
170
|
+
if (r.authenticated) {
|
|
171
|
+
result = r;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
if (r.strategyCrash === true) {
|
|
175
|
+
crashCount++;
|
|
176
|
+
console.error(`[warden] strategy '${strategyName}' threw during verify(): ${r.error ?? "unknown error"}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
// AuthManager rethrows structured WardenError sentinels — treat these
|
|
181
|
+
// as crashes too (config errors, SessionStrategy.USE_LOGIN, etc.).
|
|
182
|
+
crashCount++;
|
|
183
|
+
console.error(`[warden] strategy '${strategyName}' threw during verify():`, err);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return { result, attemptCount, crashCount };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Gate the authenticated user against the route's @Permission / @Role decorators.
|
|
190
|
+
* Both are independent AND gates resolved ONCE against the same unified
|
|
191
|
+
* EffectivePermissions set (Epic 56, D7). Returns the FORBIDDEN message, or null
|
|
192
|
+
* when authorised (and zero-cost when neither decorator is present).
|
|
193
|
+
*/
|
|
194
|
+
async function checkAuthorization(auth, user, controller, action) {
|
|
195
|
+
const requiredPermissions = getPermissionMetadata(controller, action);
|
|
196
|
+
const requiredRoles = getRoleMetadata(controller, action);
|
|
197
|
+
if (requiredPermissions.length === 0 && requiredRoles.length === 0) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
const effective = await auth.resolvePermissions(user, "global");
|
|
201
|
+
if (requiredPermissions.length > 0) {
|
|
202
|
+
const missing = requiredPermissions.filter((p) => !effective.has(p));
|
|
203
|
+
if (missing.length > 0)
|
|
204
|
+
return `Missing permissions: ${missing.join(", ")}`;
|
|
205
|
+
}
|
|
206
|
+
if (requiredRoles.length > 0) {
|
|
207
|
+
const missing = requiredRoles.filter((r) => !effective.roles.has(r));
|
|
208
|
+
if (missing.length > 0)
|
|
209
|
+
return `Missing roles: ${missing.join(", ")}`;
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
|
package/dist/native.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface NativeWarden {
|
|
2
|
+
jwtSign(payload: string, secret: string): string;
|
|
3
|
+
jwtVerify(token: string, secret: string): string;
|
|
4
|
+
constantTimeEq(a: string, b: string): boolean;
|
|
5
|
+
hmacSign(data: string, secret: string): string;
|
|
6
|
+
hmacVerify(data: string, signature: string, secret: string): boolean;
|
|
7
|
+
randomBytes(len: number): string;
|
|
8
|
+
randomHex(len: number): string;
|
|
9
|
+
}
|
|
10
|
+
/** Whether the Rust NAPI engine loaded. */
|
|
11
|
+
export declare function isNativeAvailable(): boolean;
|
|
12
|
+
/** Get the native engine. Returns undefined if not loaded — caller must throw. */
|
|
13
|
+
export declare function nativeWarden(): NativeWarden | undefined;
|
|
14
|
+
//# sourceMappingURL=native.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,YAAY;IAC5B,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACjD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACjD,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/C,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IACrE,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAC/B;AAaD,2CAA2C;AAC3C,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,kFAAkF;AAClF,wBAAgB,YAAY,IAAI,YAAY,GAAG,SAAS,CAEvD"}
|