@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,61 @@
1
+ /**
2
+ * GitHub OAuth2 driver for FirstContact.
3
+ */
4
+ export class GitHubDriver {
5
+ config;
6
+ constructor(config) {
7
+ this.config = config;
8
+ }
9
+ redirectUrl(state) {
10
+ const params = new URLSearchParams({
11
+ client_id: this.config.clientId,
12
+ redirect_uri: this.config.callbackUrl,
13
+ scope: (this.config.scopes ?? ["read:user", "user:email"]).join(" "),
14
+ ...(state ? { state } : {}),
15
+ });
16
+ return `https://github.com/login/oauth/authorize?${params}`;
17
+ }
18
+ async callback(code, state, expectedState) {
19
+ if (expectedState && state !== expectedState) {
20
+ throw new Error("OAuth state mismatch — possible CSRF attack");
21
+ }
22
+ const tokenRes = await fetch("https://github.com/login/oauth/access_token", {
23
+ method: "POST",
24
+ headers: {
25
+ Accept: "application/json",
26
+ "Content-Type": "application/json",
27
+ },
28
+ body: JSON.stringify({
29
+ client_id: this.config.clientId,
30
+ client_secret: this.config.clientSecret,
31
+ code,
32
+ }),
33
+ });
34
+ if (!tokenRes.ok) {
35
+ throw new Error(`GitHub OAuth token exchange failed (HTTP ${tokenRes.status})`);
36
+ }
37
+ const tokens = (await tokenRes.json());
38
+ if (!tokens.access_token)
39
+ throw new Error("GitHub OAuth: no access_token in response");
40
+ const userRes = await fetch("https://api.github.com/user", {
41
+ headers: {
42
+ Authorization: `Bearer ${tokens.access_token}`,
43
+ Accept: "application/json",
44
+ },
45
+ });
46
+ if (!userRes.ok)
47
+ throw new Error(`GitHub user API failed (${userRes.status})`);
48
+ const raw = (await userRes.json());
49
+ return {
50
+ user: {
51
+ id: String(raw.id ?? ""),
52
+ email: String(raw.email ?? ""),
53
+ name: String(raw.name ?? raw.login ?? ""),
54
+ avatarUrl: raw.avatar_url,
55
+ raw,
56
+ },
57
+ token: { accessToken: String(tokens.access_token) },
58
+ };
59
+ }
60
+ }
61
+ //# sourceMappingURL=GitHubDriver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GitHubDriver.js","sourceRoot":"","sources":["../../../src/firstcontact/drivers/GitHubDriver.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,MAAM,OAAO,YAAY;IACJ;IAApB,YAAoB,MAAmB;QAAnB,WAAM,GAAN,MAAM,CAAa;IAAG,CAAC;IAE3C,WAAW,CAAC,KAAc;QACzB,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YAClC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC/B,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;YACrC,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YACpE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3B,CAAC,CAAC;QACH,OAAO,4CAA4C,MAAM,EAAE,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,QAAQ,CACb,IAAY,EACZ,KAAc,EACd,aAAsB;QAEtB,IAAI,aAAa,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAC3B,6CAA6C,EAC7C;YACC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACR,MAAM,EAAE,kBAAkB;gBAC1B,cAAc,EAAE,kBAAkB;aAClC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACpB,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAC/B,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;gBACvC,IAAI;aACJ,CAAC;SACF,CACD,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACd,4CAA4C,QAAQ,CAAC,MAAM,GAAG,CAC9D,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAC;QAClE,IAAI,CAAC,MAAM,CAAC,YAAY;YACvB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAE9D,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,6BAA6B,EAAE;YAC1D,OAAO,EAAE;gBACR,aAAa,EAAE,UAAU,MAAM,CAAC,YAAY,EAAE;gBAC9C,MAAM,EAAE,kBAAkB;aAC1B;SACD,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,CAAC,EAAE;YACd,MAAM,IAAI,KAAK,CAAC,2BAA2B,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QAC/D,MAAM,GAAG,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAA4B,CAAC;QAE9D,OAAO;YACN,IAAI,EAAE;gBACL,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC;gBACxB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC9B,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;gBACzC,SAAS,EAAE,GAAG,CAAC,UAAgC;gBAC/C,GAAG;aACH;YACD,KAAK,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE;SACnD,CAAC;IACH,CAAC;CACD"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Google OAuth2 driver for FirstContact.
3
+ */
4
+ import type { FirstContactDriver, OAuthConfig, OAuthToken, OAuthUser } from "../types.js";
5
+ export declare class GoogleDriver implements FirstContactDriver {
6
+ private config;
7
+ constructor(config: OAuthConfig);
8
+ redirectUrl(state?: string): string;
9
+ callback(code: string, state?: string, expectedState?: string): Promise<{
10
+ user: OAuthUser;
11
+ token: OAuthToken;
12
+ }>;
13
+ }
14
+ //# sourceMappingURL=GoogleDriver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GoogleDriver.d.ts","sourceRoot":"","sources":["../../../src/firstcontact/drivers/GoogleDriver.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACX,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,SAAS,EACT,MAAM,aAAa,CAAC;AAErB,qBAAa,YAAa,YAAW,kBAAkB;IAC1C,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,WAAW;IAEvC,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM;IAY7B,QAAQ,CACb,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC;CAkDlD"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Google OAuth2 driver for FirstContact.
3
+ */
4
+ export class GoogleDriver {
5
+ config;
6
+ constructor(config) {
7
+ this.config = config;
8
+ }
9
+ redirectUrl(state) {
10
+ const params = new URLSearchParams({
11
+ client_id: this.config.clientId,
12
+ redirect_uri: this.config.callbackUrl,
13
+ response_type: "code",
14
+ scope: (this.config.scopes ?? ["openid", "email", "profile"]).join(" "),
15
+ access_type: "offline",
16
+ ...(state ? { state } : {}),
17
+ });
18
+ return `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
19
+ }
20
+ async callback(code, state, expectedState) {
21
+ // CSRF protection: validate state matches what we sent in redirectUrl().
22
+ if (expectedState && state !== expectedState) {
23
+ throw new Error("OAuth state mismatch — possible CSRF attack");
24
+ }
25
+ const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
26
+ method: "POST",
27
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
28
+ body: new URLSearchParams({
29
+ code,
30
+ client_id: this.config.clientId,
31
+ client_secret: this.config.clientSecret,
32
+ redirect_uri: this.config.callbackUrl,
33
+ grant_type: "authorization_code",
34
+ }),
35
+ });
36
+ if (!tokenRes.ok) {
37
+ throw new Error(`Google OAuth token exchange failed (HTTP ${tokenRes.status})`);
38
+ }
39
+ const tokens = (await tokenRes.json());
40
+ if (!tokens.access_token)
41
+ throw new Error("Google OAuth: no access_token in response");
42
+ const userRes = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
43
+ headers: { Authorization: `Bearer ${tokens.access_token}` },
44
+ });
45
+ if (!userRes.ok)
46
+ throw new Error(`Google userinfo failed (${userRes.status})`);
47
+ const raw = (await userRes.json());
48
+ return {
49
+ user: {
50
+ id: String(raw.id ?? ""),
51
+ email: String(raw.email ?? ""),
52
+ name: String(raw.name ?? ""),
53
+ avatarUrl: raw.picture,
54
+ raw,
55
+ },
56
+ token: {
57
+ accessToken: String(tokens.access_token),
58
+ refreshToken: tokens.refresh_token,
59
+ expiresIn: tokens.expires_in,
60
+ },
61
+ };
62
+ }
63
+ }
64
+ //# sourceMappingURL=GoogleDriver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GoogleDriver.js","sourceRoot":"","sources":["../../../src/firstcontact/drivers/GoogleDriver.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,MAAM,OAAO,YAAY;IACJ;IAApB,YAAoB,MAAmB;QAAnB,WAAM,GAAN,MAAM,CAAa;IAAG,CAAC;IAE3C,WAAW,CAAC,KAAc;QACzB,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YAClC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC/B,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;YACrC,aAAa,EAAE,MAAM;YACrB,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YACvE,WAAW,EAAE,SAAS;YACtB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3B,CAAC,CAAC;QACH,OAAO,gDAAgD,MAAM,EAAE,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,QAAQ,CACb,IAAY,EACZ,KAAc,EACd,aAAsB;QAEtB,yEAAyE;QACzE,IAAI,aAAa,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,qCAAqC,EAAE;YACnE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;YAChE,IAAI,EAAE,IAAI,eAAe,CAAC;gBACzB,IAAI;gBACJ,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAC/B,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;gBACvC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;gBACrC,UAAU,EAAE,oBAAoB;aAChC,CAAC;SACF,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACd,4CAA4C,QAAQ,CAAC,MAAM,GAAG,CAC9D,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAC;QAClE,IAAI,CAAC,MAAM,CAAC,YAAY;YACvB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAE9D,MAAM,OAAO,GAAG,MAAM,KAAK,CAC1B,+CAA+C,EAC/C;YACC,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,CAAC,YAAY,EAAE,EAAE;SAC3D,CACD,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,EAAE;YACd,MAAM,IAAI,KAAK,CAAC,2BAA2B,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QAC/D,MAAM,GAAG,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAA4B,CAAC;QAE9D,OAAO;YACN,IAAI,EAAE;gBACL,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC;gBACxB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC9B,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;gBAC5B,SAAS,EAAE,GAAG,CAAC,OAA6B;gBAC5C,GAAG;aACH;YACD,KAAK,EAAE;gBACN,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;gBACxC,YAAY,EAAE,MAAM,CAAC,aAAmC;gBACxD,SAAS,EAAE,MAAM,CAAC,UAAgC;aAClD;SACD,CAAC;IACH,CAAC;CACD"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * FirstContact — OAuth2 social authentication types.
3
+ */
4
+ export interface OAuthConfig {
5
+ clientId: string;
6
+ clientSecret: string;
7
+ callbackUrl: string;
8
+ scopes?: string[];
9
+ }
10
+ export interface OAuthUser {
11
+ id: string;
12
+ email: string;
13
+ name: string;
14
+ avatarUrl?: string;
15
+ raw: Record<string, unknown>;
16
+ }
17
+ export interface OAuthToken {
18
+ accessToken: string;
19
+ refreshToken?: string;
20
+ expiresIn?: number;
21
+ }
22
+ export interface FirstContactDriver {
23
+ redirectUrl(state?: string): string;
24
+ /**
25
+ * Handle the OAuth callback. `state` MUST be validated against the value
26
+ * originally passed to `redirectUrl()` to prevent CSRF login attacks.
27
+ * Throws if state is missing or mismatched.
28
+ */
29
+ callback(code: string, state?: string, expectedState?: string): Promise<{
30
+ user: OAuthUser;
31
+ token: OAuthToken;
32
+ }>;
33
+ }
34
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/firstcontact/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAClC,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CACP,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;CACnD"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * FirstContact — OAuth2 social authentication types.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/firstcontact/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @module @c9up/warden
3
+ * @description Warden — Authentication & authorization for the Ream framework
4
+ * @implements FR48, FR49, FR50, FR51, FR52, FR53
5
+ */
6
+ export type { AuthConfig, AuthResult, AuthStrategy, UserPayload, } from "./AuthManager.js";
7
+ export { AuthManager } from "./AuthManager.js";
8
+ export type { AuthRateLimiterConfig } from "./AuthRateLimiter.js";
9
+ export { AuthRateLimiter } from "./AuthRateLimiter.js";
10
+ export { AuthorizationResponse } from "./bouncer/AuthorizationResponse.js";
11
+ export { BasePolicy } from "./bouncer/BasePolicy.js";
12
+ export { Bouncer } from "./bouncer/Bouncer.js";
13
+ export { action, allowGuest, getActionMetadata, } from "./bouncer/decorators.js";
14
+ export type { PolicyAuthorizer } from "./bouncer/PolicyAuthorizer.js";
15
+ export type { Ability, AbilityOptions, AuthorizerResponse, BouncerContext, } from "./bouncer/types.js";
16
+ export { defineConfig } from "./config.js";
17
+ export { configure } from "./configure.js";
18
+ export { WardenError } from "./errors.js";
19
+ export { GitHubDriver } from "./firstcontact/drivers/GitHubDriver.js";
20
+ export { GoogleDriver } from "./firstcontact/drivers/GoogleDriver.js";
21
+ export { FirstContactManager } from "./firstcontact/FirstContactManager.js";
22
+ export type { FirstContactDriver, OAuthConfig, OAuthToken, OAuthUser, } from "./firstcontact/types.js";
23
+ export { Guard, getGuardMetadata, getPermissionMetadata, getRoleMetadata, Permission, Role, } from "./Guard.js";
24
+ export type { RefreshTokenDriver, StoredRefreshToken, } from "./RefreshTokenStore.js";
25
+ export { generateRefreshToken, MemoryRefreshTokenDriver, } from "./RefreshTokenStore.js";
26
+ export { MemoryRightsStore } from "./rights/MemoryRightsStore.js";
27
+ export { RightsResolver } from "./rights/RightsResolver.js";
28
+ export type { EffectivePermissions, RightsStore, Scope, } from "./rights/types.js";
29
+ export { scopeKey } from "./rights/types.js";
30
+ export type { ApiKeyConfig } from "./strategies/ApiKeyStrategy.js";
31
+ export { ApiKeyStrategy } from "./strategies/ApiKeyStrategy.js";
32
+ export type { JwtStrategyConfig } from "./strategies/JwtStrategy.js";
33
+ export { generateJwtSecret, JwtStrategy } from "./strategies/JwtStrategy.js";
34
+ export type { SessionStore, SessionStrategyConfig, } from "./strategies/SessionStrategy.js";
35
+ export { SessionStrategy } from "./strategies/SessionStrategy.js";
36
+ export type { BlacklistDriver } from "./TokenBlacklist.js";
37
+ export { MemoryBlacklistDriver, TokenBlacklist } from "./TokenBlacklist.js";
38
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,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,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,GACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,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,eAAe,EACf,UAAU,EACV,IAAI,GACJ,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,kBAAkB,EAClB,kBAAkB,GAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,oBAAoB,EACpB,wBAAwB,GACxB,MAAM,wBAAwB,CAAC;AAChC,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,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,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"}
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @module @c9up/warden
3
+ * @description Warden — Authentication & authorization for the Ream framework
4
+ * @implements FR48, FR49, FR50, FR51, FR52, FR53
5
+ */
6
+ export { AuthManager } from "./AuthManager.js";
7
+ export { AuthRateLimiter } from "./AuthRateLimiter.js";
8
+ export { AuthorizationResponse } from "./bouncer/AuthorizationResponse.js";
9
+ export { BasePolicy } from "./bouncer/BasePolicy.js";
10
+ export { Bouncer } from "./bouncer/Bouncer.js";
11
+ export { action, allowGuest, getActionMetadata, } from "./bouncer/decorators.js";
12
+ export { defineConfig } from "./config.js";
13
+ export { configure } from "./configure.js";
14
+ export { WardenError } from "./errors.js";
15
+ export { GitHubDriver } from "./firstcontact/drivers/GitHubDriver.js";
16
+ export { GoogleDriver } from "./firstcontact/drivers/GoogleDriver.js";
17
+ export { FirstContactManager } from "./firstcontact/FirstContactManager.js";
18
+ export { Guard, getGuardMetadata, getPermissionMetadata, getRoleMetadata, Permission, Role, } from "./Guard.js";
19
+ export { generateRefreshToken, MemoryRefreshTokenDriver, } from "./RefreshTokenStore.js";
20
+ export { MemoryRightsStore } from "./rights/MemoryRightsStore.js";
21
+ export { RightsResolver } from "./rights/RightsResolver.js";
22
+ export { scopeKey } from "./rights/types.js";
23
+ export { ApiKeyStrategy } from "./strategies/ApiKeyStrategy.js";
24
+ export { generateJwtSecret, JwtStrategy } from "./strategies/JwtStrategy.js";
25
+ export { SessionStrategy } from "./strategies/SessionStrategy.js";
26
+ export { MemoryBlacklistDriver, TokenBlacklist } from "./TokenBlacklist.js";
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,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;AAQjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,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,eAAe,EACf,UAAU,EACV,IAAI,GACJ,MAAM,YAAY,CAAC;AAKpB,OAAO,EACN,oBAAoB,EACpB,wBAAwB,GACxB,MAAM,wBAAwB,CAAC;AAChC,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"}
@@ -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;AAM1B,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,iBAoG1E"}
@@ -0,0 +1,201 @@
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, 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
+ }
111
+ // Auth successful — attach to context and continue.
112
+ ctx.auth = result;
113
+ await next();
114
+ }
115
+ /**
116
+ * Try each declared strategy in order — session strategies via
117
+ * `verifyWithContext()`, others via `verify(token)` with native-first credential
118
+ * fallback. Distinguishes crashes (strategy threw / `strategyCrash`) from
119
+ * credential rejections so the caller can return 500 vs 401.
120
+ */
121
+ async function tryAuthenticate(auth, strategies, creds) {
122
+ const { bearerToken, apiKey, session } = creds;
123
+ let result = null;
124
+ let attemptCount = 0;
125
+ let crashCount = 0;
126
+ for (const strategyName of strategies) {
127
+ try {
128
+ let r;
129
+ if (strategyName === "session") {
130
+ const strategy = auth.getStrategy(strategyName);
131
+ const verifyWithContext = strategy && hasVerifyWithContext(strategy)
132
+ ? strategy.verifyWithContext
133
+ : undefined;
134
+ if (verifyWithContext) {
135
+ attemptCount++;
136
+ r = await verifyWithContext.call(strategy, "", { session });
137
+ // The session path bypasses AuthManager.verify(), so apply the
138
+ // same prototype-pollution guard JWT / api-key users get there.
139
+ if (r.user)
140
+ sanitizePayload(r.user);
141
+ }
142
+ else {
143
+ continue;
144
+ }
145
+ }
146
+ else {
147
+ // Native-first credential, other transport as fallback so a
148
+ // single-credential client still authenticates (and an invalid
149
+ // Bearer no longer masks a valid API key for the api-key strategy).
150
+ const credential = strategyName === "api-key"
151
+ ? apiKey || bearerToken
152
+ : bearerToken || apiKey;
153
+ if (!credential)
154
+ continue;
155
+ attemptCount++;
156
+ r = await auth.verify(credential, strategyName);
157
+ }
158
+ if (r.authenticated) {
159
+ result = r;
160
+ break;
161
+ }
162
+ if (r.strategyCrash === true) {
163
+ crashCount++;
164
+ console.error(`[warden] strategy '${strategyName}' threw during verify(): ${r.error ?? "unknown error"}`);
165
+ }
166
+ }
167
+ catch (err) {
168
+ // AuthManager rethrows structured WardenError sentinels — treat these
169
+ // as crashes too (config errors, SessionStrategy.USE_LOGIN, etc.).
170
+ crashCount++;
171
+ console.error(`[warden] strategy '${strategyName}' threw during verify():`, err);
172
+ }
173
+ }
174
+ return { result, attemptCount, crashCount };
175
+ }
176
+ /**
177
+ * Gate the authenticated user against the route's @Permission / @Role decorators.
178
+ * Both are independent AND gates resolved ONCE against the same unified
179
+ * EffectivePermissions set (Epic 56, D7). Returns the FORBIDDEN message, or null
180
+ * when authorised (and zero-cost when neither decorator is present).
181
+ */
182
+ async function checkAuthorization(auth, user, controller, action) {
183
+ const requiredPermissions = getPermissionMetadata(controller, action);
184
+ const requiredRoles = getRoleMetadata(controller, action);
185
+ if (requiredPermissions.length === 0 && requiredRoles.length === 0) {
186
+ return null;
187
+ }
188
+ const effective = await auth.resolvePermissions(user, "global");
189
+ if (requiredPermissions.length > 0) {
190
+ const missing = requiredPermissions.filter((p) => !effective.has(p));
191
+ if (missing.length > 0)
192
+ return `Missing permissions: ${missing.join(", ")}`;
193
+ }
194
+ if (requiredRoles.length > 0) {
195
+ const missing = requiredRoles.filter((r) => !effective.roles.has(r));
196
+ if (missing.length > 0)
197
+ return `Missing roles: ${missing.join(", ")}`;
198
+ }
199
+ return null;
200
+ }
201
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
@@ -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"}
package/dist/native.js ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Warden NAPI loader — loads the `warden-engine-napi` binary for Rust-native
3
+ * JWT/HMAC/random operations. There is no TS fallback: when the binary is
4
+ * absent, `nativeWarden()` returns `undefined` and `JwtStrategy` (and any
5
+ * other consumer) throws `WARDEN_NAPI_REQUIRED` at use time. Password
6
+ * hashing is the sole responsibility of `@c9up/sigil` (story 40.1).
7
+ */
8
+ import { createRequire } from "node:module";
9
+ import { dirname, join } from "node:path";
10
+ import { arch, platform } from "node:process";
11
+ import { fileURLToPath } from "node:url";
12
+ const nodeRequire = createRequire(import.meta.url);
13
+ const currentDir = dirname(fileURLToPath(import.meta.url));
14
+ const platformMap = {
15
+ "linux-x64": "linux-x64-gnu",
16
+ "linux-arm64": "linux-arm64-gnu",
17
+ "darwin-x64": "darwin-x64",
18
+ "darwin-arm64": "darwin-arm64",
19
+ "win32-x64": "win32-x64-msvc",
20
+ };
21
+ let native;
22
+ try {
23
+ const suffix = platformMap[`${platform}-${arch}`];
24
+ if (suffix) {
25
+ native = nodeRequire(join(currentDir, `../index.${suffix}.node`));
26
+ }
27
+ }
28
+ catch {
29
+ // Binary not available — JwtStrategy will throw WARDEN_NAPI_REQUIRED at use time.
30
+ }
31
+ /** Whether the Rust NAPI engine loaded. */
32
+ export function isNativeAvailable() {
33
+ return native !== undefined;
34
+ }
35
+ /** Get the native engine. Returns undefined if not loaded — caller must throw. */
36
+ export function nativeWarden() {
37
+ return native;
38
+ }
39
+ //# sourceMappingURL=native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnD,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE3D,MAAM,WAAW,GAA2B;IAC3C,WAAW,EAAE,eAAe;IAC5B,aAAa,EAAE,iBAAiB;IAChC,YAAY,EAAE,YAAY;IAC1B,cAAc,EAAE,cAAc;IAC9B,WAAW,EAAE,gBAAgB;CAC7B,CAAC;AAYF,IAAI,MAAgC,CAAC;AAErC,IAAI,CAAC;IACJ,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,QAAQ,IAAI,IAAI,EAAE,CAAC,CAAC;IAClD,IAAI,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,MAAM,OAAO,CAAC,CAAC,CAAC;IACnE,CAAC;AACF,CAAC;AAAC,MAAM,CAAC;IACR,kFAAkF;AACnF,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,iBAAiB;IAChC,OAAO,MAAM,KAAK,SAAS,CAAC;AAC7B,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,YAAY;IAC3B,OAAO,MAAM,CAAC;AACf,CAAC"}