@c9up/warden 0.1.17 → 0.1.19
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/AuthManager.d.ts +86 -1
- package/dist/AuthManager.d.ts.map +1 -1
- package/dist/AuthManager.js +113 -10
- package/dist/AuthManager.js.map +1 -1
- package/dist/Authenticator.d.ts +13 -1
- package/dist/Authenticator.d.ts.map +1 -1
- package/dist/Authenticator.js +37 -3
- package/dist/Authenticator.js.map +1 -1
- package/dist/RememberMeToken.d.ts +169 -0
- package/dist/RememberMeToken.d.ts.map +1 -0
- package/dist/RememberMeToken.js +269 -0
- package/dist/RememberMeToken.js.map +1 -0
- package/dist/ResilientBlacklistDriver.d.ts +15 -9
- package/dist/ResilientBlacklistDriver.d.ts.map +1 -1
- package/dist/ResilientBlacklistDriver.js +53 -15
- package/dist/ResilientBlacklistDriver.js.map +1 -1
- package/dist/Secret.d.ts +11 -0
- package/dist/Secret.d.ts.map +1 -0
- package/dist/Secret.js +36 -0
- package/dist/Secret.js.map +1 -0
- package/dist/WardenProvider.d.ts +1 -0
- package/dist/WardenProvider.d.ts.map +1 -1
- package/dist/WardenProvider.js +64 -0
- package/dist/WardenProvider.js.map +1 -1
- package/dist/bouncer/Bouncer.d.ts +46 -1
- package/dist/bouncer/Bouncer.d.ts.map +1 -1
- package/dist/bouncer/Bouncer.js +77 -4
- package/dist/bouncer/Bouncer.js.map +1 -1
- package/dist/bouncer/PolicyAuthorizer.d.ts +9 -1
- package/dist/bouncer/PolicyAuthorizer.d.ts.map +1 -1
- package/dist/bouncer/PolicyAuthorizer.js +17 -1
- package/dist/bouncer/PolicyAuthorizer.js.map +1 -1
- package/dist/bouncer/evaluate.d.ts +14 -2
- package/dist/bouncer/evaluate.d.ts.map +1 -1
- package/dist/bouncer/evaluate.js +16 -5
- package/dist/bouncer/evaluate.js.map +1 -1
- package/dist/bouncer/types.d.ts +4 -2
- package/dist/bouncer/types.d.ts.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -1
- package/dist/firstcontact/drivers/GitHubDriver.d.ts.map +1 -1
- package/dist/firstcontact/drivers/GitHubDriver.js +2 -3
- package/dist/firstcontact/drivers/GitHubDriver.js.map +1 -1
- package/dist/firstcontact/drivers/GoogleDriver.d.ts.map +1 -1
- package/dist/firstcontact/drivers/GoogleDriver.js +2 -3
- package/dist/firstcontact/drivers/GoogleDriver.js.map +1 -1
- package/dist/firstcontact/types.d.ts +10 -0
- package/dist/firstcontact/types.d.ts.map +1 -1
- package/dist/firstcontact/types.js +18 -1
- package/dist/firstcontact/types.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/mfa/MfaManager.d.ts.map +1 -1
- package/dist/mfa/MfaManager.js +14 -5
- package/dist/mfa/MfaManager.js.map +1 -1
- package/dist/mfa/TotpProvider.d.ts +34 -1
- package/dist/mfa/TotpProvider.d.ts.map +1 -1
- package/dist/mfa/TotpProvider.js +60 -4
- package/dist/mfa/TotpProvider.js.map +1 -1
- package/dist/mfa/webauthn-codec.d.ts +6 -1
- package/dist/mfa/webauthn-codec.d.ts.map +1 -1
- package/dist/mfa/webauthn-codec.js +50 -6
- package/dist/mfa/webauthn-codec.js.map +1 -1
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +46 -1
- package/dist/middleware.js.map +1 -1
- package/dist/quasar.d.ts.map +1 -1
- package/dist/quasar.js +6 -14
- package/dist/quasar.js.map +1 -1
- package/dist/sanitize.d.ts +10 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +15 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/strategies/ApiKeyStrategy.d.ts +10 -1
- package/dist/strategies/ApiKeyStrategy.d.ts.map +1 -1
- package/dist/strategies/ApiKeyStrategy.js +16 -0
- package/dist/strategies/ApiKeyStrategy.js.map +1 -1
- package/dist/strategies/BasicAuthStrategy.d.ts +59 -0
- package/dist/strategies/BasicAuthStrategy.d.ts.map +1 -0
- package/dist/strategies/BasicAuthStrategy.js +97 -0
- package/dist/strategies/BasicAuthStrategy.js.map +1 -0
- package/dist/strategies/JwtStrategy.d.ts +10 -1
- package/dist/strategies/JwtStrategy.d.ts.map +1 -1
- package/dist/strategies/JwtStrategy.js +11 -0
- package/dist/strategies/JwtStrategy.js.map +1 -1
- package/dist/strategies/SessionStrategy.d.ts +84 -2
- package/dist/strategies/SessionStrategy.d.ts.map +1 -1
- package/dist/strategies/SessionStrategy.js +124 -1
- package/dist/strategies/SessionStrategy.js.map +1 -1
- 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 +4 -2
- package/src/AuthManager.ts +163 -7
- package/src/Authenticator.ts +58 -10
- package/src/RememberMeToken.ts +357 -0
- package/src/ResilientBlacklistDriver.ts +58 -18
- package/src/Secret.ts +41 -0
- package/src/WardenProvider.ts +90 -0
- package/src/bouncer/Bouncer.ts +93 -4
- package/src/bouncer/PolicyAuthorizer.ts +20 -1
- package/src/bouncer/evaluate.ts +26 -6
- package/src/bouncer/types.ts +4 -2
- package/src/config.ts +9 -0
- package/src/firstcontact/drivers/GitHubDriver.ts +2 -3
- package/src/firstcontact/drivers/GoogleDriver.ts +2 -3
- package/src/firstcontact/types.ts +24 -0
- package/src/index.ts +30 -2
- package/src/mfa/MfaManager.ts +15 -8
- package/src/mfa/TotpProvider.ts +90 -6
- package/src/mfa/webauthn-codec.ts +48 -6
- package/src/middleware.ts +55 -2
- package/src/quasar.ts +6 -14
- package/src/sanitize.ts +17 -0
- package/src/strategies/ApiKeyStrategy.ts +23 -1
- package/src/strategies/BasicAuthStrategy.ts +127 -0
- package/src/strategies/JwtStrategy.ts +18 -1
- package/src/strategies/SessionStrategy.ts +167 -2
|
@@ -7,8 +7,19 @@
|
|
|
7
7
|
* @implements MISS-7
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
AuthClientResponse,
|
|
12
|
+
AuthResult,
|
|
13
|
+
AuthStrategy,
|
|
14
|
+
UserPayload,
|
|
15
|
+
} from "../AuthManager.js";
|
|
11
16
|
import { WardenError } from "../errors.js";
|
|
17
|
+
import {
|
|
18
|
+
decodeTokenValue,
|
|
19
|
+
mintRememberMeToken,
|
|
20
|
+
type RememberMeTokenDriver,
|
|
21
|
+
verifyAndRecycleRememberMeToken,
|
|
22
|
+
} from "../RememberMeToken.js";
|
|
12
23
|
|
|
13
24
|
export interface SessionStore {
|
|
14
25
|
get(key: string): unknown;
|
|
@@ -33,18 +44,148 @@ export interface SessionStore {
|
|
|
33
44
|
export interface SessionStrategyConfig {
|
|
34
45
|
sessionKey?: string;
|
|
35
46
|
findUser: (id: string | number) => Promise<UserPayload | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Turn on "keep me signed in". Adonis calls the equivalent flag
|
|
49
|
+
* `useRememberMeTokens`; without a driver there is nowhere to persist the
|
|
50
|
+
* token, so passing one IS the opt-in.
|
|
51
|
+
*/
|
|
52
|
+
rememberMeTokens?: RememberMeTokenDriver;
|
|
53
|
+
/**
|
|
54
|
+
* How long a remember-me token lives. Adonis defaults to two years, and a
|
|
55
|
+
* number here is SECONDS (`durationToSeconds` semantics).
|
|
56
|
+
*/
|
|
57
|
+
rememberMeAge?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Cookie name holding the token. Adonis derives `remember_<guard>`; the
|
|
60
|
+
* default matches the `web` guard an app is most likely migrating.
|
|
61
|
+
*/
|
|
62
|
+
rememberMeCookieName?: string;
|
|
36
63
|
}
|
|
37
64
|
|
|
65
|
+
/** Two years, Adonis' `rememberMeTokensAge` default. */
|
|
66
|
+
const DEFAULT_REMEMBER_AGE_SECONDS = 63_072_000;
|
|
67
|
+
|
|
38
68
|
export class SessionStrategy implements AuthStrategy {
|
|
39
69
|
name = "session";
|
|
40
70
|
#config: SessionStrategyConfig;
|
|
41
71
|
#sessionKey: string;
|
|
72
|
+
#viaRemember = false;
|
|
73
|
+
#loggedOut = false;
|
|
74
|
+
#attemptedViaRemember = false;
|
|
42
75
|
|
|
43
76
|
constructor(config: SessionStrategyConfig) {
|
|
44
77
|
this.#config = config;
|
|
45
78
|
this.#sessionKey = config.sessionKey ?? "auth_user_id";
|
|
46
79
|
}
|
|
47
80
|
|
|
81
|
+
/** The cookie the remember-me token travels in. */
|
|
82
|
+
get rememberMeCookieName(): string {
|
|
83
|
+
return this.#config.rememberMeCookieName ?? "remember_web";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Whether "keep me signed in" is wired at all. */
|
|
87
|
+
get usesRememberMeTokens(): boolean {
|
|
88
|
+
return this.#config.rememberMeTokens !== undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Whether the current user was revived from a remember-me cookie rather
|
|
93
|
+
* than signing in (AdonisJS `viaRemember`).
|
|
94
|
+
*
|
|
95
|
+
* This is the distinction that lets an app demand the password again before
|
|
96
|
+
* something sensitive — changing an email, spending money, deleting an
|
|
97
|
+
* account. Nothing reported it, so a session restored from a cookie looked
|
|
98
|
+
* exactly like one where the user had just typed their password.
|
|
99
|
+
*/
|
|
100
|
+
get viaRemember(): boolean {
|
|
101
|
+
return this.#viaRemember;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Whether a remember-me token was even tried on this request (AdonisJS
|
|
106
|
+
* `attemptedViaRemember`) — true whether or not it worked.
|
|
107
|
+
*/
|
|
108
|
+
get attemptedViaRemember(): boolean {
|
|
109
|
+
return this.#attemptedViaRemember;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Whether `logout()` ran on this guard (AdonisJS `isLoggedOut`).
|
|
114
|
+
*
|
|
115
|
+
* A handler that logs out and then keeps working — clearing a cart, writing
|
|
116
|
+
* an audit line — could not tell that the session was already gone.
|
|
117
|
+
*/
|
|
118
|
+
get isLoggedOut(): boolean {
|
|
119
|
+
return this.#loggedOut;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** The session key the user id is stored under (AdonisJS `sessionKeyName`). */
|
|
123
|
+
get sessionKeyName(): string {
|
|
124
|
+
return this.#sessionKey;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** The cookie key the remember-me token is stored under (AdonisJS `rememberMeKeyName`). */
|
|
128
|
+
get rememberMeKeyName(): string {
|
|
129
|
+
return this.rememberMeCookieName;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#rememberMeAge(): number {
|
|
133
|
+
return this.#config.rememberMeAge ?? DEFAULT_REMEMBER_AGE_SECONDS;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Mint a remember-me token for `user` and return the cookie value the caller
|
|
138
|
+
* must set. `null` when the feature is not wired.
|
|
139
|
+
*/
|
|
140
|
+
async issueRememberMeToken(user: UserPayload): Promise<string | null> {
|
|
141
|
+
const driver = this.#config.rememberMeTokens;
|
|
142
|
+
if (!driver) return null;
|
|
143
|
+
const minted = mintRememberMeToken(user.id, this.#rememberMeAge());
|
|
144
|
+
await driver.create(minted.stored);
|
|
145
|
+
return minted.value;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Authenticate from a remember-me cookie, and RECYCLE it: the returned
|
|
150
|
+
* `cookieValue` must replace the one the browser holds.
|
|
151
|
+
*
|
|
152
|
+
* The user is re-read through `findUser`, never trusted from the token — a
|
|
153
|
+
* token outlives the row it points at, and a deleted or disabled account
|
|
154
|
+
* must not walk back in through a cookie.
|
|
155
|
+
*/
|
|
156
|
+
async authenticateViaRememberMeToken(
|
|
157
|
+
cookieValue: unknown,
|
|
158
|
+
): Promise<{ user: UserPayload; cookieValue: string } | null> {
|
|
159
|
+
const driver = this.#config.rememberMeTokens;
|
|
160
|
+
if (!driver) return null;
|
|
161
|
+
this.#attemptedViaRemember = true;
|
|
162
|
+
|
|
163
|
+
const recycled = await verifyAndRecycleRememberMeToken(
|
|
164
|
+
driver,
|
|
165
|
+
cookieValue,
|
|
166
|
+
this.#rememberMeAge(),
|
|
167
|
+
);
|
|
168
|
+
if (!recycled) return null;
|
|
169
|
+
|
|
170
|
+
const user = await this.#config.findUser(recycled.userId);
|
|
171
|
+
if (!user) return null;
|
|
172
|
+
|
|
173
|
+
this.#viaRemember = true;
|
|
174
|
+
return { user, cookieValue: recycled.value };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Drop the token behind `cookieValue`. Called on logout so the persistent
|
|
179
|
+
* credential dies with the session — Adonis deletes the row too.
|
|
180
|
+
*/
|
|
181
|
+
async revokeRememberMeToken(cookieValue: unknown): Promise<void> {
|
|
182
|
+
const driver = this.#config.rememberMeTokens;
|
|
183
|
+
if (!driver) return;
|
|
184
|
+
const decoded = decodeTokenValue(cookieValue);
|
|
185
|
+
if (!decoded) return;
|
|
186
|
+
await driver.delete(decoded.identifier);
|
|
187
|
+
}
|
|
188
|
+
|
|
48
189
|
/** Authenticate via email/password — stores user ID in session. */
|
|
49
190
|
async authenticate(
|
|
50
191
|
_credentials: { email: string; password: string },
|
|
@@ -102,10 +243,34 @@ export class SessionStrategy implements AuthStrategy {
|
|
|
102
243
|
async login(user: UserPayload, session: SessionStore): Promise<void> {
|
|
103
244
|
session.regenerate();
|
|
104
245
|
session.put(this.#sessionKey, user.id);
|
|
246
|
+
this.#loggedOut = false;
|
|
247
|
+
// A password was typed: this session is no longer "via remember", even
|
|
248
|
+
// if a cookie was tried earlier in the same request. Without the reset
|
|
249
|
+
// the flag would stay true and a re-auth prompt would never fire.
|
|
250
|
+
this.#viaRemember = false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* The session entry a test client needs to be `user` (AdonisJS
|
|
255
|
+
* `authenticateAsClient`).
|
|
256
|
+
*
|
|
257
|
+
* Returns the same key `login()` writes, so the request is authenticated by
|
|
258
|
+
* the guard's own logic rather than by the test knowing where the id lives.
|
|
259
|
+
*/
|
|
260
|
+
authenticateAsClient(user: UserPayload): AuthClientResponse {
|
|
261
|
+
return { session: { [this.#sessionKey]: user.id } };
|
|
105
262
|
}
|
|
106
263
|
|
|
107
|
-
/**
|
|
264
|
+
/**
|
|
265
|
+
* Logout — remove the user id from the session.
|
|
266
|
+
*
|
|
267
|
+
* The remember-me token is revoked separately through
|
|
268
|
+
* {@link revokeRememberMeToken}, because only the caller holds the cookie.
|
|
269
|
+
*/
|
|
108
270
|
async logout(session: SessionStore): Promise<void> {
|
|
109
271
|
session.forget(this.#sessionKey);
|
|
272
|
+
this.#viaRemember = false;
|
|
273
|
+
this.#attemptedViaRemember = false;
|
|
274
|
+
this.#loggedOut = true;
|
|
110
275
|
}
|
|
111
276
|
}
|