@c9up/warden 0.1.16 → 0.1.17
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/dist/RedisBlacklistDriver.d.ts +7 -1
- package/dist/RedisBlacklistDriver.d.ts.map +1 -1
- package/dist/RedisBlacklistDriver.js +30 -5
- package/dist/RedisBlacklistDriver.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/quasar.d.ts +18 -0
- package/dist/quasar.d.ts.map +1 -0
- package/dist/quasar.js +64 -0
- package/dist/quasar.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 +6 -2
- package/src/RedisBlacklistDriver.ts +39 -5
- package/src/index.ts +1 -0
- package/src/quasar.ts +86 -0
|
@@ -17,9 +17,15 @@ export interface RedisBlacklistConfig {
|
|
|
17
17
|
/** Key prefix for blacklist entries. Default `warden:blacklist:`. */
|
|
18
18
|
prefix?: string;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Where the client comes from. A resolver is what lets the blacklist name its
|
|
22
|
+
* connection instead of being handed a client: config is read synchronously,
|
|
23
|
+
* the first revocation check is not.
|
|
24
|
+
*/
|
|
25
|
+
export type RedisClientSource = RedisLikeClient | (() => RedisLikeClient | Promise<RedisLikeClient>);
|
|
20
26
|
export declare class RedisBlacklistDriver implements BlacklistDriver {
|
|
21
27
|
#private;
|
|
22
|
-
constructor(
|
|
28
|
+
constructor(source: RedisClientSource, config?: RedisBlacklistConfig);
|
|
23
29
|
/**
|
|
24
30
|
* Blacklist a JTI until its natural expiry. Tokens already past `expiresAt`
|
|
25
31
|
* are skipped — there is nothing left to revoke.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RedisBlacklistDriver.d.ts","sourceRoot":"","sources":["../src/RedisBlacklistDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC/B,GAAG,CACF,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,oBAAoB;IACpC,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,oBAAqB,YAAW,eAAe;;
|
|
1
|
+
{"version":3,"file":"RedisBlacklistDriver.d.ts","sourceRoot":"","sources":["../src/RedisBlacklistDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC/B,GAAG,CACF,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,oBAAoB;IACpC,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAC1B,eAAe,GACf,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC;AAEtD,qBAAa,oBAAqB,YAAW,eAAe;;gBAM/C,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAE,oBAAyB;IA0BxE;;;OAGG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASlD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKxC,yDAAyD;IACnD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAK9B"}
|
|
@@ -8,12 +8,35 @@
|
|
|
8
8
|
* `cleanup()` is a no-op.
|
|
9
9
|
*/
|
|
10
10
|
export class RedisBlacklistDriver {
|
|
11
|
-
#
|
|
11
|
+
#source;
|
|
12
|
+
#resolved;
|
|
13
|
+
#pending;
|
|
12
14
|
#prefix;
|
|
13
|
-
constructor(
|
|
14
|
-
this.#
|
|
15
|
+
constructor(source, config = {}) {
|
|
16
|
+
this.#source = source;
|
|
15
17
|
this.#prefix = config.prefix ?? "warden:blacklist:";
|
|
16
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* The client, resolved once. Two requests racing on a cold start must not
|
|
21
|
+
* each open their own connection, so the in-flight promise is shared.
|
|
22
|
+
*/
|
|
23
|
+
async #client() {
|
|
24
|
+
if (this.#resolved)
|
|
25
|
+
return this.#resolved;
|
|
26
|
+
if (typeof this.#source !== "function") {
|
|
27
|
+
this.#resolved = this.#source;
|
|
28
|
+
return this.#resolved;
|
|
29
|
+
}
|
|
30
|
+
if (!this.#pending) {
|
|
31
|
+
const resolver = this.#source;
|
|
32
|
+
this.#pending = Promise.resolve(resolver()).then((client) => {
|
|
33
|
+
this.#resolved = client;
|
|
34
|
+
this.#pending = undefined;
|
|
35
|
+
return client;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return this.#pending;
|
|
39
|
+
}
|
|
17
40
|
/**
|
|
18
41
|
* Blacklist a JTI until its natural expiry. Tokens already past `expiresAt`
|
|
19
42
|
* are skipped — there is nothing left to revoke.
|
|
@@ -23,10 +46,12 @@ export class RedisBlacklistDriver {
|
|
|
23
46
|
if (ttlSeconds <= 0) {
|
|
24
47
|
return;
|
|
25
48
|
}
|
|
26
|
-
await this.#client
|
|
49
|
+
const client = await this.#client();
|
|
50
|
+
await client.set(this.#key(jti), "1", "EX", ttlSeconds);
|
|
27
51
|
}
|
|
28
52
|
async has(jti) {
|
|
29
|
-
|
|
53
|
+
const client = await this.#client();
|
|
54
|
+
return (await client.exists(this.#key(jti))) > 0;
|
|
30
55
|
}
|
|
31
56
|
/** No-op: Redis/KeyDB evicts expired keys on its own. */
|
|
32
57
|
async cleanup() { }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RedisBlacklistDriver.js","sourceRoot":"","sources":["../src/RedisBlacklistDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"RedisBlacklistDriver.js","sourceRoot":"","sources":["../src/RedisBlacklistDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA6BH,MAAM,OAAO,oBAAoB;IACvB,OAAO,CAAoB;IACpC,SAAS,CAA8B;IACvC,QAAQ,CAAuC;IACtC,OAAO,CAAS;IAEzB,YAAY,MAAyB,EAAE,SAA+B,EAAE;QACvE,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,mBAAmB,CAAC;IACrD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO;QACZ,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC;QAC1C,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACxC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC;YAC9B,OAAO,IAAI,CAAC,SAAS,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC;YAC9B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;gBAC3D,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC;gBACxB,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;gBAC1B,OAAO,MAAM,CAAC;YACf,CAAC,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,SAAiB;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QAC9D,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;YACrB,OAAO;QACR,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACpC,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACpB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACpC,OAAO,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAClD,CAAC;IAED,yDAAyD;IACzD,KAAK,CAAC,OAAO,KAAmB,CAAC;IAEjC,IAAI,CAAC,GAAW;QACf,OAAO,GAAG,IAAI,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC;IAChC,CAAC;CACD"}
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export type { TotpAlgorithm, TotpConfig, TotpEnrollment, } from "./mfa/TotpProvi
|
|
|
35
35
|
export { TotpProvider } from "./mfa/TotpProvider.js";
|
|
36
36
|
export type { AuthenticationOptionsJSON, AuthenticationResponseJSON, RegistrationOptionsJSON, RegistrationResponseJSON, StoredPasskey, WebauthnChallengeStore, WebauthnConfig, WebauthnCredentialStore, WebauthnUser, } from "./mfa/WebauthnProvider.js";
|
|
37
37
|
export { MemoryWebauthnChallengeStore, MemoryWebauthnCredentialStore, WebauthnProvider, } from "./mfa/WebauthnProvider.js";
|
|
38
|
+
export { quasarConnection } from "./quasar.js";
|
|
38
39
|
export type { RedisBlacklistConfig, RedisLikeClient, } from "./RedisBlacklistDriver.js";
|
|
39
40
|
export { RedisBlacklistDriver } from "./RedisBlacklistDriver.js";
|
|
40
41
|
export type { RefreshTokenDriver, StoredRefreshToken, } from "./RefreshTokenStore.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EACN,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,aAAa,GACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACX,UAAU,EACV,UAAU,EACV,YAAY,EACZ,WAAW,GACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EACN,MAAM,EACN,UAAU,EACV,iBAAiB,GACjB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EACX,OAAO,EACP,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,uBAAuB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACN,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,YAAY,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,GACX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAC5E,YAAY,EACX,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,SAAS,GACT,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACN,KAAK,EACL,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,UAAU,EACV,IAAI,GACJ,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,GACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,YAAY,EACX,SAAS,EACT,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,gBAAgB,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACvE,YAAY,EACX,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,eAAe,GACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC5E,YAAY,EACX,aAAa,EACb,UAAU,EACV,cAAc,GACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,YAAY,EACX,yBAAyB,EACzB,0BAA0B,EAC1B,uBAAuB,EACvB,wBAAwB,EACxB,aAAa,EACb,sBAAsB,EACtB,cAAc,EACd,uBAAuB,EACvB,YAAY,GACZ,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,4BAA4B,EAC5B,6BAA6B,EAC7B,gBAAgB,GAChB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACX,oBAAoB,EACpB,eAAe,GACf,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,YAAY,EACX,kBAAkB,EAClB,kBAAkB,GAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,oBAAoB,EACpB,wBAAwB,GACxB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACX,qBAAqB,EACrB,wBAAwB,GACxB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,YAAY,EACX,oBAAoB,EACpB,WAAW,EACX,KAAK,GACL,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC7E,YAAY,EACX,YAAY,EACZ,qBAAqB,GACrB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ export { MemoryMfaFactorStore, MfaManager } from "./mfa/MfaManager.js";
|
|
|
23
23
|
export { MemoryOtpChallengeStore, OtpProvider } from "./mfa/OtpProvider.js";
|
|
24
24
|
export { TotpProvider } from "./mfa/TotpProvider.js";
|
|
25
25
|
export { MemoryWebauthnChallengeStore, MemoryWebauthnCredentialStore, WebauthnProvider, } from "./mfa/WebauthnProvider.js";
|
|
26
|
+
export { quasarConnection } from "./quasar.js";
|
|
26
27
|
export { RedisBlacklistDriver } from "./RedisBlacklistDriver.js";
|
|
27
28
|
export { generateRefreshToken, MemoryRefreshTokenDriver, } from "./RefreshTokenStore.js";
|
|
28
29
|
export { ResilientBlacklistDriver } from "./ResilientBlacklistDriver.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACN,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,aAAa,GACb,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EACN,MAAM,EACN,UAAU,EACV,iBAAiB,GACjB,MAAM,yBAAyB,CAAC;AAWjC,OAAO,EACN,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,YAAY,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,GACX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAO5E,OAAO,EACN,KAAK,EACL,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,UAAU,EACV,IAAI,GACJ,MAAM,YAAY,CAAC;AAMpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAQnE,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAUvE,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAM5E,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAYrD,OAAO,EACN,4BAA4B,EAC5B,6BAA6B,EAC7B,gBAAgB,GAChB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACN,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,aAAa,GACb,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EACN,MAAM,EACN,UAAU,EACV,iBAAiB,GACjB,MAAM,yBAAyB,CAAC;AAWjC,OAAO,EACN,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,YAAY,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,GACX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAO5E,OAAO,EACN,KAAK,EACL,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,UAAU,EACV,IAAI,GACJ,MAAM,YAAY,CAAC;AAMpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAQnE,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAUvE,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAM5E,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAYrD,OAAO,EACN,4BAA4B,EAC5B,6BAA6B,EAC7B,gBAAgB,GAChB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAK/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAKjE,OAAO,EACN,oBAAoB,EACpB,wBAAwB,GACxB,MAAM,wBAAwB,CAAC;AAKhC,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAM5D,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAK7E,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAElE,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/quasar.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolving a Redis connection by name, from `@c9up/quasar`.
|
|
3
|
+
*
|
|
4
|
+
* Warden does not depend on quasar: it is an optional peer, and this module
|
|
5
|
+
* never imports it statically. The specifier is built at runtime so the
|
|
6
|
+
* TypeScript build stays free of it too — a hard type import would make echo
|
|
7
|
+
* unbuildable for anyone whose blacklist is in memory.
|
|
8
|
+
*
|
|
9
|
+
* The shape is checked before use rather than asserted, the same way warden
|
|
10
|
+
* duck-types its host framework.
|
|
11
|
+
*/
|
|
12
|
+
import type { RedisLikeClient } from "./RedisBlacklistDriver.js";
|
|
13
|
+
/**
|
|
14
|
+
* A resolver for `new RedisBlacklistDriver(quasarConnection())` — quasar is
|
|
15
|
+
* loaded on the first revocation check, not at config time.
|
|
16
|
+
*/
|
|
17
|
+
export declare function quasarConnection(name?: string): () => Promise<RedisLikeClient>;
|
|
18
|
+
//# sourceMappingURL=quasar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quasar.d.ts","sourceRoot":"","sources":["../src/quasar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAoCjE;;;GAGG;AACH,wBAAgB,gBAAgB,CAC/B,IAAI,CAAC,EAAE,MAAM,GACX,MAAM,OAAO,CAAC,eAAe,CAAC,CA+BhC"}
|
package/dist/quasar.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolving a Redis connection by name, from `@c9up/quasar`.
|
|
3
|
+
*
|
|
4
|
+
* Warden does not depend on quasar: it is an optional peer, and this module
|
|
5
|
+
* never imports it statically. The specifier is built at runtime so the
|
|
6
|
+
* TypeScript build stays free of it too — a hard type import would make echo
|
|
7
|
+
* unbuildable for anyone whose blacklist is in memory.
|
|
8
|
+
*
|
|
9
|
+
* The shape is checked before use rather than asserted, the same way warden
|
|
10
|
+
* duck-types its host framework.
|
|
11
|
+
*/
|
|
12
|
+
function isConnectionSource(value) {
|
|
13
|
+
return (typeof value === "object" &&
|
|
14
|
+
value !== null &&
|
|
15
|
+
typeof Reflect.get(value, "connection") === "function");
|
|
16
|
+
}
|
|
17
|
+
function isRedisLikeClient(value) {
|
|
18
|
+
if (typeof value !== "object" || value === null)
|
|
19
|
+
return false;
|
|
20
|
+
// The commands this driver actually issues. A connection missing one of
|
|
21
|
+
// them would fail on the first revocation, far from the cause.
|
|
22
|
+
const required = [
|
|
23
|
+
"get",
|
|
24
|
+
"set",
|
|
25
|
+
"del",
|
|
26
|
+
"exists",
|
|
27
|
+
"keys",
|
|
28
|
+
"sadd",
|
|
29
|
+
"srem",
|
|
30
|
+
"smembers",
|
|
31
|
+
"expire",
|
|
32
|
+
"ttl",
|
|
33
|
+
];
|
|
34
|
+
return required.every((name) => typeof Reflect.get(value, name) === "function");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A resolver for `new RedisBlacklistDriver(quasarConnection())` — quasar is
|
|
38
|
+
* loaded on the first revocation check, not at config time.
|
|
39
|
+
*/
|
|
40
|
+
export function quasarConnection(name) {
|
|
41
|
+
return async () => {
|
|
42
|
+
const specifier = "@c9up/quasar/services/main";
|
|
43
|
+
let loaded;
|
|
44
|
+
try {
|
|
45
|
+
loaded = await import(/* @vite-ignore */ specifier);
|
|
46
|
+
}
|
|
47
|
+
catch (cause) {
|
|
48
|
+
throw new Error(`Warden: the "${name ?? "default"}" blacklist asks for a quasar connection, but @c9up/quasar is not installed.\n` +
|
|
49
|
+
" pnpm add @c9up/quasar", { cause });
|
|
50
|
+
}
|
|
51
|
+
const manager = isConnectionSource(loaded)
|
|
52
|
+
? loaded
|
|
53
|
+
: Reflect.get(Object(loaded), "default");
|
|
54
|
+
if (!isConnectionSource(manager)) {
|
|
55
|
+
throw new Error("Warden: @c9up/quasar/services/main did not expose a connection() manager");
|
|
56
|
+
}
|
|
57
|
+
const connection = manager.connection(name);
|
|
58
|
+
if (!isRedisLikeClient(connection)) {
|
|
59
|
+
throw new Error(`Warden: quasar connection "${name ?? "default"}" does not carry the commands this blacklist needs`);
|
|
60
|
+
}
|
|
61
|
+
return connection;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=quasar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quasar.js","sourceRoot":"","sources":["../src/quasar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH,SAAS,kBAAkB,CAAC,KAAc;IACzC,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,UAAU,CACtD,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAc;IACxC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,wEAAwE;IACxE,+DAA+D;IAC/D,MAAM,QAAQ,GAAG;QAChB,KAAK;QACL,KAAK;QACL,KAAK;QACL,QAAQ;QACR,MAAM;QACN,MAAM;QACN,MAAM;QACN,UAAU;QACV,QAAQ;QACR,KAAK;KACL,CAAC;IACF,OAAO,QAAQ,CAAC,KAAK,CACpB,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,UAAU,CACxD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC/B,IAAa;IAEb,OAAO,KAAK,IAAI,EAAE;QACjB,MAAM,SAAS,GAAG,4BAA4B,CAAC;QAC/C,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACJ,MAAM,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACd,gBAAgB,IAAI,IAAI,SAAS,gFAAgF;gBAChH,yBAAyB,EAC1B,EAAE,KAAK,EAAE,CACT,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC;YACzC,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC;QAC1C,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACd,0EAA0E,CAC1E,CAAC;QACH,CAAC;QAED,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACd,8BAA8B,IAAI,IAAI,SAAS,oDAAoD,CACnG,CAAC;QACH,CAAC;QACD,OAAO,UAAU,CAAC;IACnB,CAAC,CAAC;AACH,CAAC"}
|
package/index.darwin-arm64.node
CHANGED
|
Binary file
|
package/index.darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
package/index.linux-x64-gnu.node
CHANGED
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c9up/warden",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Warden — Authentication & authorization for the Ream framework",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -49,11 +49,15 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"@c9up/ream": "^0.1.10"
|
|
52
|
+
"@c9up/ream": "^0.1.10",
|
|
53
|
+
"@c9up/quasar": "^0.1.0"
|
|
53
54
|
},
|
|
54
55
|
"peerDependenciesMeta": {
|
|
55
56
|
"@c9up/ream": {
|
|
56
57
|
"optional": true
|
|
58
|
+
},
|
|
59
|
+
"@c9up/quasar": {
|
|
60
|
+
"optional": true
|
|
57
61
|
}
|
|
58
62
|
},
|
|
59
63
|
"devDependencies": {
|
|
@@ -26,15 +26,47 @@ export interface RedisBlacklistConfig {
|
|
|
26
26
|
prefix?: string;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Where the client comes from. A resolver is what lets the blacklist name its
|
|
31
|
+
* connection instead of being handed a client: config is read synchronously,
|
|
32
|
+
* the first revocation check is not.
|
|
33
|
+
*/
|
|
34
|
+
export type RedisClientSource =
|
|
35
|
+
| RedisLikeClient
|
|
36
|
+
| (() => RedisLikeClient | Promise<RedisLikeClient>);
|
|
37
|
+
|
|
29
38
|
export class RedisBlacklistDriver implements BlacklistDriver {
|
|
30
|
-
readonly #
|
|
39
|
+
readonly #source: RedisClientSource;
|
|
40
|
+
#resolved: RedisLikeClient | undefined;
|
|
41
|
+
#pending: Promise<RedisLikeClient> | undefined;
|
|
31
42
|
readonly #prefix: string;
|
|
32
43
|
|
|
33
|
-
constructor(
|
|
34
|
-
this.#
|
|
44
|
+
constructor(source: RedisClientSource, config: RedisBlacklistConfig = {}) {
|
|
45
|
+
this.#source = source;
|
|
35
46
|
this.#prefix = config.prefix ?? "warden:blacklist:";
|
|
36
47
|
}
|
|
37
48
|
|
|
49
|
+
/**
|
|
50
|
+
* The client, resolved once. Two requests racing on a cold start must not
|
|
51
|
+
* each open their own connection, so the in-flight promise is shared.
|
|
52
|
+
*/
|
|
53
|
+
async #client(): Promise<RedisLikeClient> {
|
|
54
|
+
if (this.#resolved) return this.#resolved;
|
|
55
|
+
if (typeof this.#source !== "function") {
|
|
56
|
+
this.#resolved = this.#source;
|
|
57
|
+
return this.#resolved;
|
|
58
|
+
}
|
|
59
|
+
if (!this.#pending) {
|
|
60
|
+
const resolver = this.#source;
|
|
61
|
+
this.#pending = Promise.resolve(resolver()).then((client) => {
|
|
62
|
+
this.#resolved = client;
|
|
63
|
+
this.#pending = undefined;
|
|
64
|
+
return client;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return this.#pending;
|
|
68
|
+
}
|
|
69
|
+
|
|
38
70
|
/**
|
|
39
71
|
* Blacklist a JTI until its natural expiry. Tokens already past `expiresAt`
|
|
40
72
|
* are skipped — there is nothing left to revoke.
|
|
@@ -44,11 +76,13 @@ export class RedisBlacklistDriver implements BlacklistDriver {
|
|
|
44
76
|
if (ttlSeconds <= 0) {
|
|
45
77
|
return;
|
|
46
78
|
}
|
|
47
|
-
await this.#client
|
|
79
|
+
const client = await this.#client();
|
|
80
|
+
await client.set(this.#key(jti), "1", "EX", ttlSeconds);
|
|
48
81
|
}
|
|
49
82
|
|
|
50
83
|
async has(jti: string): Promise<boolean> {
|
|
51
|
-
|
|
84
|
+
const client = await this.#client();
|
|
85
|
+
return (await client.exists(this.#key(jti))) > 0;
|
|
52
86
|
}
|
|
53
87
|
|
|
54
88
|
/** No-op: Redis/KeyDB evicts expired keys on its own. */
|
package/src/index.ts
CHANGED
package/src/quasar.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolving a Redis connection by name, from `@c9up/quasar`.
|
|
3
|
+
*
|
|
4
|
+
* Warden does not depend on quasar: it is an optional peer, and this module
|
|
5
|
+
* never imports it statically. The specifier is built at runtime so the
|
|
6
|
+
* TypeScript build stays free of it too — a hard type import would make echo
|
|
7
|
+
* unbuildable for anyone whose blacklist is in memory.
|
|
8
|
+
*
|
|
9
|
+
* The shape is checked before use rather than asserted, the same way warden
|
|
10
|
+
* duck-types its host framework.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { RedisLikeClient } from "./RedisBlacklistDriver.js";
|
|
14
|
+
|
|
15
|
+
/** The slice of quasar's manager this needs: a connection, by name. */
|
|
16
|
+
interface ConnectionSource {
|
|
17
|
+
connection(name?: string): unknown;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isConnectionSource(value: unknown): value is ConnectionSource {
|
|
21
|
+
return (
|
|
22
|
+
typeof value === "object" &&
|
|
23
|
+
value !== null &&
|
|
24
|
+
typeof Reflect.get(value, "connection") === "function"
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isRedisLikeClient(value: unknown): value is RedisLikeClient {
|
|
29
|
+
if (typeof value !== "object" || value === null) return false;
|
|
30
|
+
// The commands this driver actually issues. A connection missing one of
|
|
31
|
+
// them would fail on the first revocation, far from the cause.
|
|
32
|
+
const required = [
|
|
33
|
+
"get",
|
|
34
|
+
"set",
|
|
35
|
+
"del",
|
|
36
|
+
"exists",
|
|
37
|
+
"keys",
|
|
38
|
+
"sadd",
|
|
39
|
+
"srem",
|
|
40
|
+
"smembers",
|
|
41
|
+
"expire",
|
|
42
|
+
"ttl",
|
|
43
|
+
];
|
|
44
|
+
return required.every(
|
|
45
|
+
(name) => typeof Reflect.get(value, name) === "function",
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A resolver for `new RedisBlacklistDriver(quasarConnection())` — quasar is
|
|
51
|
+
* loaded on the first revocation check, not at config time.
|
|
52
|
+
*/
|
|
53
|
+
export function quasarConnection(
|
|
54
|
+
name?: string,
|
|
55
|
+
): () => Promise<RedisLikeClient> {
|
|
56
|
+
return async () => {
|
|
57
|
+
const specifier = "@c9up/quasar/services/main";
|
|
58
|
+
let loaded: unknown;
|
|
59
|
+
try {
|
|
60
|
+
loaded = await import(/* @vite-ignore */ specifier);
|
|
61
|
+
} catch (cause) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Warden: the "${name ?? "default"}" blacklist asks for a quasar connection, but @c9up/quasar is not installed.\n` +
|
|
64
|
+
" pnpm add @c9up/quasar",
|
|
65
|
+
{ cause },
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const manager = isConnectionSource(loaded)
|
|
70
|
+
? loaded
|
|
71
|
+
: Reflect.get(Object(loaded), "default");
|
|
72
|
+
if (!isConnectionSource(manager)) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
"Warden: @c9up/quasar/services/main did not expose a connection() manager",
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const connection = manager.connection(name);
|
|
79
|
+
if (!isRedisLikeClient(connection)) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Warden: quasar connection "${name ?? "default"}" does not carry the commands this blacklist needs`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
return connection;
|
|
85
|
+
};
|
|
86
|
+
}
|