@c9up/warden 0.1.3 → 0.1.4

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 (134) hide show
  1. package/dist/AuthManager.d.ts +98 -0
  2. package/dist/AuthManager.d.ts.map +1 -0
  3. package/dist/AuthManager.js +147 -0
  4. package/dist/AuthManager.js.map +1 -0
  5. package/dist/AuthRateLimiter.d.ts +29 -0
  6. package/dist/AuthRateLimiter.d.ts.map +1 -0
  7. package/dist/AuthRateLimiter.js +88 -0
  8. package/dist/AuthRateLimiter.js.map +1 -0
  9. package/dist/Guard.d.ts +26 -0
  10. package/dist/Guard.d.ts.map +1 -0
  11. package/dist/Guard.js +52 -0
  12. package/dist/Guard.js.map +1 -0
  13. package/dist/RefreshTokenStore.d.ts +34 -0
  14. package/dist/RefreshTokenStore.d.ts.map +1 -0
  15. package/dist/RefreshTokenStore.js +48 -0
  16. package/dist/RefreshTokenStore.js.map +1 -0
  17. package/dist/TokenBlacklist.d.ts +30 -0
  18. package/dist/TokenBlacklist.d.ts.map +1 -0
  19. package/dist/TokenBlacklist.js +50 -0
  20. package/dist/TokenBlacklist.js.map +1 -0
  21. package/dist/WardenProvider.d.ts +19 -0
  22. package/dist/WardenProvider.d.ts.map +1 -0
  23. package/dist/WardenProvider.js +61 -0
  24. package/dist/WardenProvider.js.map +1 -0
  25. package/dist/bouncer/AuthorizationResponse.d.ts +25 -0
  26. package/dist/bouncer/AuthorizationResponse.d.ts.map +1 -0
  27. package/dist/bouncer/AuthorizationResponse.js +31 -0
  28. package/dist/bouncer/AuthorizationResponse.js.map +1 -0
  29. package/dist/bouncer/BasePolicy.d.ts +47 -0
  30. package/dist/bouncer/BasePolicy.d.ts.map +1 -0
  31. package/dist/bouncer/BasePolicy.js +36 -0
  32. package/dist/bouncer/BasePolicy.js.map +1 -0
  33. package/dist/bouncer/Bouncer.d.ts +47 -0
  34. package/dist/bouncer/Bouncer.d.ts.map +1 -0
  35. package/dist/bouncer/Bouncer.js +134 -0
  36. package/dist/bouncer/Bouncer.js.map +1 -0
  37. package/dist/bouncer/PolicyAuthorizer.d.ts +24 -0
  38. package/dist/bouncer/PolicyAuthorizer.d.ts.map +1 -0
  39. package/dist/bouncer/PolicyAuthorizer.js +92 -0
  40. package/dist/bouncer/PolicyAuthorizer.js.map +1 -0
  41. package/dist/bouncer/decorators.d.ts +25 -0
  42. package/dist/bouncer/decorators.d.ts.map +1 -0
  43. package/dist/bouncer/decorators.js +36 -0
  44. package/dist/bouncer/decorators.js.map +1 -0
  45. package/dist/bouncer/evaluate.d.ts +36 -0
  46. package/dist/bouncer/evaluate.d.ts.map +1 -0
  47. package/dist/bouncer/evaluate.js +64 -0
  48. package/dist/bouncer/evaluate.js.map +1 -0
  49. package/dist/bouncer/policyContext.d.ts +29 -0
  50. package/dist/bouncer/policyContext.d.ts.map +1 -0
  51. package/dist/bouncer/policyContext.js +36 -0
  52. package/dist/bouncer/policyContext.js.map +1 -0
  53. package/dist/bouncer/types.d.ts +52 -0
  54. package/dist/bouncer/types.d.ts.map +1 -0
  55. package/dist/bouncer/types.js +9 -0
  56. package/dist/bouncer/types.js.map +1 -0
  57. package/dist/config.d.ts +42 -0
  58. package/dist/config.d.ts.map +1 -0
  59. package/dist/config.js +21 -0
  60. package/dist/config.js.map +1 -0
  61. package/dist/configure.d.ts +10 -0
  62. package/dist/configure.d.ts.map +1 -0
  63. package/dist/configure.js +44 -0
  64. package/dist/configure.js.map +1 -0
  65. package/dist/errors.d.ts +18 -0
  66. package/dist/errors.d.ts.map +1 -0
  67. package/dist/errors.js +21 -0
  68. package/dist/errors.js.map +1 -0
  69. package/dist/firstcontact/FirstContactManager.d.ts +25 -0
  70. package/dist/firstcontact/FirstContactManager.d.ts.map +1 -0
  71. package/dist/firstcontact/FirstContactManager.js +38 -0
  72. package/dist/firstcontact/FirstContactManager.js.map +1 -0
  73. package/dist/firstcontact/drivers/GitHubDriver.d.ts +14 -0
  74. package/dist/firstcontact/drivers/GitHubDriver.d.ts.map +1 -0
  75. package/dist/firstcontact/drivers/GitHubDriver.js +61 -0
  76. package/dist/firstcontact/drivers/GitHubDriver.js.map +1 -0
  77. package/dist/firstcontact/drivers/GoogleDriver.d.ts +14 -0
  78. package/dist/firstcontact/drivers/GoogleDriver.d.ts.map +1 -0
  79. package/dist/firstcontact/drivers/GoogleDriver.js +64 -0
  80. package/dist/firstcontact/drivers/GoogleDriver.js.map +1 -0
  81. package/dist/firstcontact/types.d.ts +34 -0
  82. package/dist/firstcontact/types.d.ts.map +1 -0
  83. package/dist/firstcontact/types.js +5 -0
  84. package/dist/firstcontact/types.js.map +1 -0
  85. package/dist/index.d.ts +38 -0
  86. package/dist/index.d.ts.map +1 -0
  87. package/dist/index.js +27 -0
  88. package/dist/index.js.map +1 -0
  89. package/dist/middleware.d.ts +51 -0
  90. package/dist/middleware.d.ts.map +1 -0
  91. package/dist/middleware.js +201 -0
  92. package/dist/middleware.js.map +1 -0
  93. package/dist/native.d.ts +14 -0
  94. package/dist/native.d.ts.map +1 -0
  95. package/dist/native.js +39 -0
  96. package/dist/native.js.map +1 -0
  97. package/dist/rights/MemoryRightsStore.d.ts +29 -0
  98. package/dist/rights/MemoryRightsStore.d.ts.map +1 -0
  99. package/dist/rights/MemoryRightsStore.js +70 -0
  100. package/dist/rights/MemoryRightsStore.js.map +1 -0
  101. package/dist/rights/RightsResolver.d.ts +29 -0
  102. package/dist/rights/RightsResolver.d.ts.map +1 -0
  103. package/dist/rights/RightsResolver.js +73 -0
  104. package/dist/rights/RightsResolver.js.map +1 -0
  105. package/dist/rights/types.d.ts +55 -0
  106. package/dist/rights/types.d.ts.map +1 -0
  107. package/dist/rights/types.js +18 -0
  108. package/dist/rights/types.js.map +1 -0
  109. package/dist/services/main.d.ts +19 -0
  110. package/dist/services/main.d.ts.map +1 -0
  111. package/dist/services/main.js +32 -0
  112. package/dist/services/main.js.map +1 -0
  113. package/dist/standalone.d.ts +37 -0
  114. package/dist/standalone.d.ts.map +1 -0
  115. package/dist/standalone.js +92 -0
  116. package/dist/standalone.js.map +1 -0
  117. package/dist/strategies/ApiKeyStrategy.d.ts +28 -0
  118. package/dist/strategies/ApiKeyStrategy.d.ts.map +1 -0
  119. package/dist/strategies/ApiKeyStrategy.js +36 -0
  120. package/dist/strategies/ApiKeyStrategy.js.map +1 -0
  121. package/dist/strategies/JwtStrategy.d.ts +51 -0
  122. package/dist/strategies/JwtStrategy.d.ts.map +1 -0
  123. package/dist/strategies/JwtStrategy.js +150 -0
  124. package/dist/strategies/JwtStrategy.js.map +1 -0
  125. package/dist/strategies/SessionStrategy.d.ts +58 -0
  126. package/dist/strategies/SessionStrategy.d.ts.map +1 -0
  127. package/dist/strategies/SessionStrategy.js +70 -0
  128. package/dist/strategies/SessionStrategy.js.map +1 -0
  129. package/index.darwin-arm64.node +0 -0
  130. package/index.darwin-x64.node +0 -0
  131. package/index.linux-arm64-gnu.node +0 -0
  132. package/index.linux-x64-gnu.node +0 -0
  133. package/index.win32-x64-msvc.node +0 -0
  134. package/package.json +1 -1
@@ -0,0 +1,150 @@
1
+ /**
2
+ * JwtStrategy — HMAC-SHA256 JWT authentication strategy.
3
+ * All signing and verification runs through the Rust warden-engine via NAPI.
4
+ */
5
+ import { randomBytes, randomUUID } from "node:crypto";
6
+ import { nativeWarden } from "../native.js";
7
+ function sign(payload, secret) {
8
+ const rust = nativeWarden();
9
+ if (!rust) {
10
+ throw new Error("[WARDEN_NAPI_REQUIRED] The Rust warden-engine binary is required. Build it with `cd packages/warden && pnpm build:napi`.");
11
+ }
12
+ return rust.jwtSign(JSON.stringify(payload), secret);
13
+ }
14
+ function verify(token, secret) {
15
+ const rust = nativeWarden();
16
+ if (!rust) {
17
+ throw new Error("[WARDEN_NAPI_REQUIRED] The Rust warden-engine binary is required. Build it with `cd packages/warden && pnpm build:napi`.");
18
+ }
19
+ try {
20
+ const payloadJson = rust.jwtVerify(token, secret);
21
+ const payload = JSON.parse(payloadJson);
22
+ if (typeof payload.sub !== "string" || !payload.sub)
23
+ return null;
24
+ return payload;
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ }
30
+ export class JwtStrategy {
31
+ name = "jwt";
32
+ #secret;
33
+ /**
34
+ * Verify-only secrets ordered active-first. Mirrors `config.secret` at
35
+ * index 0 plus any `previousSecrets`. Built once at construction so
36
+ * `verify()` doesn't re-allocate per call.
37
+ */
38
+ #verifySecrets;
39
+ #expiresIn;
40
+ #findUser;
41
+ #verifyCredentials;
42
+ #blacklist;
43
+ constructor(config) {
44
+ if (config.secret.length < 32) {
45
+ throw new Error("JWT secret must be at least 32 characters");
46
+ }
47
+ for (const prev of config.previousSecrets ?? []) {
48
+ if (prev.length < 32) {
49
+ throw new Error("JWT previousSecrets entries must be at least 32 characters");
50
+ }
51
+ }
52
+ this.#secret = config.secret;
53
+ this.#verifySecrets = [config.secret, ...(config.previousSecrets ?? [])];
54
+ this.#expiresIn = config.expiresInSeconds ?? 3600;
55
+ this.#findUser = config.findUser;
56
+ this.#verifyCredentials = config.verifyCredentials;
57
+ this.#blacklist = config.blacklist;
58
+ }
59
+ /**
60
+ * Try each accepted secret in order. First successful verify wins.
61
+ * Returns `null` when no secret accepts the token — same semantics as
62
+ * the single-secret path so callers see "invalid or expired token".
63
+ */
64
+ #verifyWithRotation(token) {
65
+ for (const secret of this.#verifySecrets) {
66
+ const payload = verify(token, secret);
67
+ if (payload)
68
+ return payload;
69
+ }
70
+ return null;
71
+ }
72
+ async authenticate(credentials) {
73
+ const email = typeof credentials.email === "string" ? credentials.email : null;
74
+ const password = typeof credentials.password === "string" ? credentials.password : null;
75
+ if (!email || !password) {
76
+ return { authenticated: false, error: "Email and password are required" };
77
+ }
78
+ const user = await this.#verifyCredentials(email, password);
79
+ if (!user) {
80
+ return { authenticated: false, error: "Invalid credentials" };
81
+ }
82
+ const token = this.signToken(user);
83
+ return { authenticated: true, user: { ...user, token } };
84
+ }
85
+ async verify(token) {
86
+ const payload = this.#verifyWithRotation(token);
87
+ if (!payload) {
88
+ return { authenticated: false, error: "Invalid or expired token" };
89
+ }
90
+ // Blacklist check runs BEFORE findUser so a revoked token never
91
+ // triggers a user lookup (cheaper, and avoids leaking user existence
92
+ // via timing differences between revoked and unknown-user paths).
93
+ if (this.#blacklist) {
94
+ // A token with no `jti` cannot be checked against the blacklist
95
+ // (`isRevoked(undefined)` always returns false), so it could
96
+ // never be revoked. When revocation is configured we must NOT
97
+ // trust such a token — reject it rather than let an
98
+ // unrevocable token through. (signToken always sets a jti;
99
+ // this guards legacy / externally-signed tokens.)
100
+ if (typeof payload.jti !== "string" || payload.jti.length === 0) {
101
+ return { authenticated: false, error: "Token missing jti claim" };
102
+ }
103
+ if (await this.#blacklist.isRevoked(payload.jti)) {
104
+ return { authenticated: false, error: "Token revoked" };
105
+ }
106
+ }
107
+ const user = await this.#findUser(payload.sub);
108
+ if (!user) {
109
+ return { authenticated: false, error: "User not found" };
110
+ }
111
+ return { authenticated: true, user };
112
+ }
113
+ signToken(user) {
114
+ const now = Math.floor(Date.now() / 1000);
115
+ return sign({
116
+ sub: user.id,
117
+ roles: user.roles,
118
+ permissions: user.permissions,
119
+ iat: now,
120
+ exp: now + this.#expiresIn,
121
+ jti: randomUUID(),
122
+ }, this.#secret);
123
+ }
124
+ /**
125
+ * Revoke a token until its `exp` claim. Requires the strategy to be
126
+ * constructed with a `blacklist` driver — otherwise throws so that a
127
+ * caller assuming revocation does not silently succeed against
128
+ * a no-op implementation.
129
+ *
130
+ * Returns `true` when the token was added to the blacklist, `false` when
131
+ * the token is already expired (revocation is unnecessary) or unparseable.
132
+ */
133
+ async revoke(token) {
134
+ if (!this.#blacklist) {
135
+ throw new Error("JwtStrategy.revoke() requires a `blacklist` driver. Pass `{ blacklist: new TokenBlacklist(driver) }` to the constructor.");
136
+ }
137
+ const payload = this.#verifyWithRotation(token);
138
+ if (!payload)
139
+ return false;
140
+ const expMs = payload.exp * 1000;
141
+ if (expMs <= Date.now())
142
+ return false;
143
+ await this.#blacklist.revoke(payload.jti, expMs);
144
+ return true;
145
+ }
146
+ }
147
+ export function generateJwtSecret() {
148
+ return randomBytes(48).toString("base64url");
149
+ }
150
+ //# sourceMappingURL=JwtStrategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JwtStrategy.js","sourceRoot":"","sources":["../../src/strategies/JwtStrategy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Session-based authentication strategy.
3
+ *
4
+ * AdonisJS session guard pattern:
5
+ * login stores userId in session, subsequent requests check session.
6
+ *
7
+ * @implements MISS-7
8
+ */
9
+ import type { AuthResult, AuthStrategy, UserPayload } from "../AuthManager.js";
10
+ export interface SessionStore {
11
+ get(key: string): unknown;
12
+ set(key: string, value: unknown): void;
13
+ forget(key: string): void;
14
+ /**
15
+ * Rotate the session id while preserving the data. REQUIRED to mitigate
16
+ * session fixation (CWE-384) at login. Ream's `Session.regenerate()`
17
+ * implements this; adapters around other session stores MUST too. The
18
+ * default ream → warden plumbing exposes the `Session` instance
19
+ * directly so this is wired automatically.
20
+ */
21
+ regenerate(): void;
22
+ }
23
+ export interface SessionStrategyConfig {
24
+ sessionKey?: string;
25
+ findUser: (id: string | number) => Promise<UserPayload | null>;
26
+ }
27
+ export declare class SessionStrategy implements AuthStrategy {
28
+ #private;
29
+ name: string;
30
+ constructor(config: SessionStrategyConfig);
31
+ /** Authenticate via email/password — stores user ID in session. */
32
+ authenticate(_credentials: {
33
+ email: string;
34
+ password: string;
35
+ }, _context?: {
36
+ session?: SessionStore;
37
+ }): Promise<AuthResult>;
38
+ /** Verify session — check if user ID is stored in session. */
39
+ verify(_token: string): Promise<AuthResult>;
40
+ /** Verify session with context — check if user ID is stored in session. */
41
+ verifyWithContext(_token: string, context?: {
42
+ session?: SessionStore;
43
+ }): Promise<AuthResult>;
44
+ /**
45
+ * Login a user — store their ID in the session.
46
+ *
47
+ * Rotates the session id BEFORE writing the authenticated user id, so an
48
+ * attacker who fed the victim a pre-login session cookie (classic
49
+ * session-fixation attack, CWE-384) ends up holding a now-discarded id
50
+ * while the victim continues under a fresh one. The driver-side cookie
51
+ * migration is handled by Ream's `SessionMiddleware` on the response
52
+ * path — see `wasRegenerated()` there.
53
+ */
54
+ login(user: UserPayload, session: SessionStore): Promise<void>;
55
+ /** Logout — remove user ID from session. */
56
+ logout(session: SessionStore): Promise<void>;
57
+ }
58
+ //# sourceMappingURL=SessionStrategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionStrategy.d.ts","sourceRoot":"","sources":["../../src/strategies/SessionStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG/E,MAAM,WAAW,YAAY;IAC5B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACvC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;;;OAMG;IACH,UAAU,IAAI,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC/D;AAED,qBAAa,eAAgB,YAAW,YAAY;;IACnD,IAAI,SAAa;gBAIL,MAAM,EAAE,qBAAqB;IAKzC,mEAAmE;IAC7D,YAAY,CACjB,YAAY,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EACjD,QAAQ,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,YAAY,CAAA;KAAE,GACnC,OAAO,CAAC,UAAU,CAAC;IAgBtB,8DAA8D;IACxD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAQjD,2EAA2E;IACrE,iBAAiB,CACtB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,YAAY,CAAA;KAAE,GAClC,OAAO,CAAC,UAAU,CAAC;IAWtB;;;;;;;;;OASG;IACG,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpE,4CAA4C;IACtC,MAAM,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAGlD"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Session-based authentication strategy.
3
+ *
4
+ * AdonisJS session guard pattern:
5
+ * login stores userId in session, subsequent requests check session.
6
+ *
7
+ * @implements MISS-7
8
+ */
9
+ import { WardenError } from "../errors.js";
10
+ export class SessionStrategy {
11
+ name = "session";
12
+ #config;
13
+ #sessionKey;
14
+ constructor(config) {
15
+ this.#config = config;
16
+ this.#sessionKey = config.sessionKey ?? "auth_user_id";
17
+ }
18
+ /** Authenticate via email/password — stores user ID in session. */
19
+ async authenticate(_credentials, _context) {
20
+ // Session strategy doesn't handle password verification — that's done
21
+ // by the caller. We throw a typed sentinel WardenError so AuthManager's
22
+ // try/catch (which swallows generic errors into a soft AuthResult)
23
+ // re-throws this one — the design boundary stays unmissable even when
24
+ // a caller routes through `authManager.authenticate()` instead of
25
+ // `strategy.authenticate()` directly.
26
+ throw new WardenError("USE_LOGIN", "SessionStrategy.authenticate() requires login() instead. Use authManager.login(user, session).", {
27
+ hint: "Verify the password yourself (e.g. via @c9up/sigil Hash.verify), then call SessionStrategy.login(user, session).",
28
+ });
29
+ }
30
+ /** Verify session — check if user ID is stored in session. */
31
+ async verify(_token) {
32
+ return {
33
+ authenticated: false,
34
+ error: "SessionStrategy.verify() requires context. Use verifyWithContext().",
35
+ };
36
+ }
37
+ /** Verify session with context — check if user ID is stored in session. */
38
+ async verifyWithContext(_token, context) {
39
+ if (!context?.session)
40
+ return { authenticated: false, error: "No session" };
41
+ const userId = context.session.get(this.#sessionKey);
42
+ if (!userId)
43
+ return { authenticated: false, error: "No session user" };
44
+ if (typeof userId !== "string" && typeof userId !== "number")
45
+ return { authenticated: false, error: "Invalid session data" };
46
+ const user = await this.#config.findUser(userId);
47
+ if (!user)
48
+ return { authenticated: false, error: "User not found" };
49
+ return { authenticated: true, user };
50
+ }
51
+ /**
52
+ * Login a user — store their ID in the session.
53
+ *
54
+ * Rotates the session id BEFORE writing the authenticated user id, so an
55
+ * attacker who fed the victim a pre-login session cookie (classic
56
+ * session-fixation attack, CWE-384) ends up holding a now-discarded id
57
+ * while the victim continues under a fresh one. The driver-side cookie
58
+ * migration is handled by Ream's `SessionMiddleware` on the response
59
+ * path — see `wasRegenerated()` there.
60
+ */
61
+ async login(user, session) {
62
+ session.regenerate();
63
+ session.set(this.#sessionKey, user.id);
64
+ }
65
+ /** Logout — remove user ID from session. */
66
+ async logout(session) {
67
+ session.forget(this.#sessionKey);
68
+ }
69
+ }
70
+ //# sourceMappingURL=SessionStrategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionStrategy.js","sourceRoot":"","sources":["../../src/strategies/SessionStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAqB3C,MAAM,OAAO,eAAe;IAC3B,IAAI,GAAG,SAAS,CAAC;IACjB,OAAO,CAAwB;IAC/B,WAAW,CAAS;IAEpB,YAAY,MAA6B;QACxC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,UAAU,IAAI,cAAc,CAAC;IACxD,CAAC;IAED,mEAAmE;IACnE,KAAK,CAAC,YAAY,CACjB,YAAiD,EACjD,QAAqC;QAErC,sEAAsE;QACtE,wEAAwE;QACxE,mEAAmE;QACnE,sEAAsE;QACtE,kEAAkE;QAClE,sCAAsC;QACtC,MAAM,IAAI,WAAW,CACpB,WAAW,EACX,gGAAgG,EAChG;YACC,IAAI,EAAE,kHAAkH;SACxH,CACD,CAAC;IACH,CAAC;IAED,8DAA8D;IAC9D,KAAK,CAAC,MAAM,CAAC,MAAc;QAC1B,OAAO;YACN,aAAa,EAAE,KAAK;YACpB,KAAK,EACJ,qEAAqE;SACtE,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,KAAK,CAAC,iBAAiB,CACtB,MAAc,EACd,OAAoC;QAEpC,IAAI,CAAC,OAAO,EAAE,OAAO;YAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QAC5E,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC;QACvE,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,KAAK,QAAQ;YAC3D,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC;QAChE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACjD,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;QACpE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACtC,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,KAAK,CAAC,IAAiB,EAAE,OAAqB;QACnD,OAAO,CAAC,UAAU,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,4CAA4C;IAC5C,KAAK,CAAC,MAAM,CAAC,OAAqB;QACjC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC;CACD"}
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c9up/warden",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Warden — Authentication for the Ream framework",
5
5
  "license": "MIT",
6
6
  "type": "module",