@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.
- 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 +26 -0
- package/dist/Guard.d.ts.map +1 -0
- package/dist/Guard.js +52 -0
- package/dist/Guard.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/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 +61 -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 +42 -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 +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +51 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +201 -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
|
@@ -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"}
|
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
|