@electr0zed/auth-gateway-cf 0.1.0

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 (110) hide show
  1. package/README.md +59 -0
  2. package/dist/auth/index.d.ts +84 -0
  3. package/dist/auth/index.d.ts.map +1 -0
  4. package/dist/auth/index.js +609 -0
  5. package/dist/auth/index.js.map +1 -0
  6. package/dist/auth/pkceState.d.ts +40 -0
  7. package/dist/auth/pkceState.d.ts.map +1 -0
  8. package/dist/auth/pkceState.js +75 -0
  9. package/dist/auth/pkceState.js.map +1 -0
  10. package/dist/config.example.d.ts +2 -0
  11. package/dist/config.example.d.ts.map +1 -0
  12. package/dist/config.example.js +83 -0
  13. package/dist/config.example.js.map +1 -0
  14. package/dist/core/gateway.d.ts +11 -0
  15. package/dist/core/gateway.d.ts.map +1 -0
  16. package/dist/core/gateway.js +97 -0
  17. package/dist/core/gateway.js.map +1 -0
  18. package/dist/do/sessionDo.d.ts +11 -0
  19. package/dist/do/sessionDo.d.ts.map +1 -0
  20. package/dist/do/sessionDo.js +96 -0
  21. package/dist/do/sessionDo.js.map +1 -0
  22. package/dist/index.d.ts +7 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +16 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/providers/baseProvider.d.ts +22 -0
  27. package/dist/providers/baseProvider.d.ts.map +1 -0
  28. package/dist/providers/baseProvider.js +129 -0
  29. package/dist/providers/baseProvider.js.map +1 -0
  30. package/dist/providers/google.d.ts +9 -0
  31. package/dist/providers/google.d.ts.map +1 -0
  32. package/dist/providers/google.js +27 -0
  33. package/dist/providers/google.js.map +1 -0
  34. package/dist/providers/index.d.ts +3 -0
  35. package/dist/providers/index.d.ts.map +1 -0
  36. package/dist/providers/index.js +5 -0
  37. package/dist/providers/index.js.map +1 -0
  38. package/dist/routing/routeMatcher.d.ts +15 -0
  39. package/dist/routing/routeMatcher.d.ts.map +1 -0
  40. package/dist/routing/routeMatcher.js +83 -0
  41. package/dist/routing/routeMatcher.js.map +1 -0
  42. package/dist/sessions/durableObjectSession.d.ts +25 -0
  43. package/dist/sessions/durableObjectSession.d.ts.map +1 -0
  44. package/dist/sessions/durableObjectSession.js +90 -0
  45. package/dist/sessions/durableObjectSession.js.map +1 -0
  46. package/dist/sessions/index.d.ts +19 -0
  47. package/dist/sessions/index.d.ts.map +1 -0
  48. package/dist/sessions/index.js +32 -0
  49. package/dist/sessions/index.js.map +1 -0
  50. package/dist/sessions/jwtSession.d.ts +19 -0
  51. package/dist/sessions/jwtSession.d.ts.map +1 -0
  52. package/dist/sessions/jwtSession.js +49 -0
  53. package/dist/sessions/jwtSession.js.map +1 -0
  54. package/dist/stores/index.d.ts +3 -0
  55. package/dist/stores/index.d.ts.map +1 -0
  56. package/dist/stores/index.js +10 -0
  57. package/dist/stores/index.js.map +1 -0
  58. package/dist/stores/postgres.d.ts +74 -0
  59. package/dist/stores/postgres.d.ts.map +1 -0
  60. package/dist/stores/postgres.js +231 -0
  61. package/dist/stores/postgres.js.map +1 -0
  62. package/dist/types.d.ts +247 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +5 -0
  65. package/dist/types.js.map +1 -0
  66. package/dist/utils/csrf.d.ts +13 -0
  67. package/dist/utils/csrf.d.ts.map +1 -0
  68. package/dist/utils/csrf.js +42 -0
  69. package/dist/utils/csrf.js.map +1 -0
  70. package/dist/utils/helpers.d.ts +8 -0
  71. package/dist/utils/helpers.d.ts.map +1 -0
  72. package/dist/utils/helpers.js +22 -0
  73. package/dist/utils/helpers.js.map +1 -0
  74. package/dist/utils/http.d.ts +9 -0
  75. package/dist/utils/http.d.ts.map +1 -0
  76. package/dist/utils/http.js +23 -0
  77. package/dist/utils/http.js.map +1 -0
  78. package/dist/utils/jwt.d.ts +22 -0
  79. package/dist/utils/jwt.d.ts.map +1 -0
  80. package/dist/utils/jwt.js +96 -0
  81. package/dist/utils/jwt.js.map +1 -0
  82. package/dist/utils/passwordPolicy.d.ts +9 -0
  83. package/dist/utils/passwordPolicy.d.ts.map +1 -0
  84. package/dist/utils/passwordPolicy.js +29 -0
  85. package/dist/utils/passwordPolicy.js.map +1 -0
  86. package/dist/utils/passwords.d.ts +33 -0
  87. package/dist/utils/passwords.d.ts.map +1 -0
  88. package/dist/utils/passwords.js +139 -0
  89. package/dist/utils/passwords.js.map +1 -0
  90. package/dist/utils/propagation.d.ts +30 -0
  91. package/dist/utils/propagation.d.ts.map +1 -0
  92. package/dist/utils/propagation.js +60 -0
  93. package/dist/utils/propagation.js.map +1 -0
  94. package/dist/utils/returnTo.d.ts +2 -0
  95. package/dist/utils/returnTo.d.ts.map +1 -0
  96. package/dist/utils/returnTo.js +21 -0
  97. package/dist/utils/returnTo.js.map +1 -0
  98. package/dist/utils/roles.d.ts +3 -0
  99. package/dist/utils/roles.d.ts.map +1 -0
  100. package/dist/utils/roles.js +25 -0
  101. package/dist/utils/roles.js.map +1 -0
  102. package/dist/utils/turnstile.d.ts +12 -0
  103. package/dist/utils/turnstile.d.ts.map +1 -0
  104. package/dist/utils/turnstile.js +40 -0
  105. package/dist/utils/turnstile.js.map +1 -0
  106. package/dist/utils/verifyInternal.d.ts +8 -0
  107. package/dist/utils/verifyInternal.d.ts.map +1 -0
  108. package/dist/utils/verifyInternal.js +69 -0
  109. package/dist/utils/verifyInternal.js.map +1 -0
  110. package/package.json +48 -0
@@ -0,0 +1,40 @@
1
+ import type { StateInfo } from '../types';
2
+ /**
3
+ * Creates a new PKCE state.
4
+ *
5
+ * @export
6
+ * @async
7
+ * @returns {Promise<{ state: string; codeChallenge: string; verifier: string }>}
8
+ */
9
+ export declare function makePkceState(): Promise<{
10
+ state: string;
11
+ codeChallenge: string;
12
+ verifier: string;
13
+ }>;
14
+ /**
15
+ * Saves a short-lived PKCE state to KV.
16
+ *
17
+ * @export
18
+ * @async
19
+ * @param {KVNamespace} kv
20
+ * @param {string} state
21
+ * @param {string} verifier
22
+ * @param {number} ttlSec
23
+ * @param {StateInfo} info
24
+ * @returns {Promise<void>}
25
+ */
26
+ export declare function saveShortState(kv: KVNamespace, state: string, verifier: string, ttlSec: number, info: StateInfo): Promise<void>;
27
+ /**
28
+ * Consumes a short-lived PKCE state from KV.
29
+ *
30
+ * @export
31
+ * @async
32
+ * @param {KVNamespace} kv
33
+ * @param {string} stateParam
34
+ * @returns {Promise<{ verifier: string; info: StateInfo }>}
35
+ */
36
+ export declare function consumeShortState(kv: KVNamespace, stateParam: string): Promise<{
37
+ verifier: string;
38
+ info: StateInfo;
39
+ }>;
40
+ //# sourceMappingURL=pkceState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkceState.d.ts","sourceRoot":"","sources":["../../src/auth/pkceState.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAyB,MAAM,UAAU,CAAC;AAEjE;;;;;;GAMG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC,CAQD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrI;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,CAoB3H"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Creates a new PKCE state.
3
+ *
4
+ * @export
5
+ * @async
6
+ * @returns {Promise<{ state: string; codeChallenge: string; verifier: string }>}
7
+ */
8
+ export async function makePkceState() {
9
+ const verBytes = crypto.getRandomValues(new Uint8Array(32));
10
+ const verifier = Array.from(verBytes)
11
+ .map((b) => b.toString(16).padStart(2, '0'))
12
+ .join('');
13
+ const state = crypto.randomUUID();
14
+ const codeChallenge = await sha256Base64Url(verifier);
15
+ return { state, codeChallenge, verifier };
16
+ }
17
+ /**
18
+ * Saves a short-lived PKCE state to KV.
19
+ *
20
+ * @export
21
+ * @async
22
+ * @param {KVNamespace} kv
23
+ * @param {string} state
24
+ * @param {string} verifier
25
+ * @param {number} ttlSec
26
+ * @param {StateInfo} info
27
+ * @returns {Promise<void>}
28
+ */
29
+ export async function saveShortState(kv, state, verifier, ttlSec, info) {
30
+ const payload = { v: verifier, i: info };
31
+ await kv.put(`_pkce:${state}`, JSON.stringify(payload), { expirationTtl: ttlSec });
32
+ }
33
+ /**
34
+ * Consumes a short-lived PKCE state from KV.
35
+ *
36
+ * @export
37
+ * @async
38
+ * @param {KVNamespace} kv
39
+ * @param {string} stateParam
40
+ * @returns {Promise<{ verifier: string; info: StateInfo }>}
41
+ */
42
+ export async function consumeShortState(kv, stateParam) {
43
+ const key = `_pkce:${stateParam}`;
44
+ const raw = await kv.get(key);
45
+ if (!raw)
46
+ throw new Error('state expired');
47
+ // Best-effort cleanup first to avoid reuse; errors ignored
48
+ await kv.delete(key).catch(() => { });
49
+ let parsed = null;
50
+ try {
51
+ parsed = JSON.parse(raw);
52
+ }
53
+ catch {
54
+ throw new Error('bad_state');
55
+ }
56
+ const verifier = parsed?.v;
57
+ const info = parsed?.i ?? { mode: 'login' };
58
+ if (!verifier)
59
+ throw new Error('bad_state');
60
+ return { verifier, info };
61
+ }
62
+ /**
63
+ * Computes the SHA-256 hash of the input and encodes it in base64url.
64
+ *
65
+ * @async
66
+ * @param {string} input
67
+ * @returns {Promise<string>}
68
+ */
69
+ async function sha256Base64Url(input) {
70
+ const data = new TextEncoder().encode(input);
71
+ const digest = await crypto.subtle.digest('SHA-256', data);
72
+ const b = String.fromCharCode(...new Uint8Array(digest));
73
+ return btoa(b).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
74
+ }
75
+ //# sourceMappingURL=pkceState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkceState.js","sourceRoot":"","sources":["../../src/auth/pkceState.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IAKlC,MAAM,QAAQ,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;SACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;IACX,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAClC,MAAM,aAAa,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;IACtD,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EAAe,EAAE,KAAa,EAAE,QAAgB,EAAE,MAAc,EAAE,IAAe;IACrH,MAAM,OAAO,GAAe,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;IACrD,MAAM,EAAE,CAAC,GAAG,CAAC,SAAS,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;AACpF,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,EAAe,EAAE,UAAkB;IAC1E,MAAM,GAAG,GAAG,SAAS,UAAU,EAAE,CAAC;IAClC,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IAE3C,2DAA2D;IAC3D,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAErC,IAAI,MAAM,GAAsB,IAAI,CAAC;IACrC,IAAI,CAAC;QACJ,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAe,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,OAAoB,EAAE,CAAC;IACzD,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC;IAE5C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IACzD,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config.example.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.example.d.ts","sourceRoot":"","sources":["../src/config.example.ts"],"names":[],"mappings":""}
@@ -0,0 +1,83 @@
1
+ import { defineConfig } from '.';
2
+ defineConfig({
3
+ projectName: 'demo-project',
4
+ publicBaseUrl: 'http://domain.com',
5
+ oAuth: {
6
+ enabled: true,
7
+ defaultProvider: 'google',
8
+ providers: [
9
+ {
10
+ id: 'google',
11
+ enabled: true,
12
+ label: 'Google',
13
+ clientId: 'clientId',
14
+ clientSecretEnv: 'GOOGLE_CLIENT_SECRET',
15
+ },
16
+ ],
17
+ successRedirectUrl: '/',
18
+ failureRedirectUrl: '/error',
19
+ },
20
+ passwordAuth: {
21
+ enabled: true,
22
+ pepperEnv: 'PASSWORD_PEPPERS',
23
+ allowSignup: true,
24
+ turnstile: {
25
+ enabled: true,
26
+ secretEnv: 'TURNSTILE_SECRET',
27
+ tokenField: 'turnstileToken',
28
+ },
29
+ },
30
+ session: {
31
+ kind: 'durableObject',
32
+ cookieName: '__Host-sid',
33
+ doName: {},
34
+ jwtSecretEnv: 'SESSION_JWT_SECRET',
35
+ idleTtlSec: 14 * 24 * 60 * 60, // 14 days
36
+ absoluteTtlSec: 30 * 24 * 60 * 60, // 30 days
37
+ issuer: 'auth-gateway',
38
+ audience: 'internal-services',
39
+ },
40
+ userStore: {
41
+ kind: 'postgres',
42
+ hyperdrive: {},
43
+ shortStateKV: {},
44
+ },
45
+ propagation: {
46
+ headerName: 'X-User',
47
+ sigHeaderName: 'X-User-Sig',
48
+ hmacSecretEnv: 'AUTH_HMAC_KEY',
49
+ },
50
+ routes: [
51
+ {
52
+ match: {
53
+ path: /^\/admin(?:\/|$)/,
54
+ },
55
+ auth: 'required',
56
+ service: {},
57
+ },
58
+ {
59
+ match: {
60
+ path: /^.*/,
61
+ },
62
+ auth: 'none',
63
+ service: {},
64
+ },
65
+ ],
66
+ overrides: {
67
+ globalUnauthenticatedRedirectUrl: '/custom-login',
68
+ accountApproval: {
69
+ enabled: true,
70
+ },
71
+ emailVerification: {
72
+ enabled: true,
73
+ requiredForLogin: true,
74
+ },
75
+ autoLoginAfterSignup: true,
76
+ captureUsername: {
77
+ enabled: true,
78
+ required: false,
79
+ minLength: 5,
80
+ },
81
+ },
82
+ });
83
+ //# sourceMappingURL=config.example.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.example.js","sourceRoot":"","sources":["../src/config.example.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,CAAC;AAEjC,YAAY,CAAC;IACZ,WAAW,EAAE,cAAc;IAC3B,aAAa,EAAE,mBAAmB;IAClC,KAAK,EAAE;QACN,OAAO,EAAE,IAAI;QACb,eAAe,EAAE,QAAQ;QACzB,SAAS,EAAE;YACV;gBACC,EAAE,EAAE,QAAQ;gBACZ,OAAO,EAAE,IAAI;gBACb,KAAK,EAAE,QAAQ;gBACf,QAAQ,EAAE,UAAU;gBACpB,eAAe,EAAE,sBAAsB;aACvC;SACD;QACD,kBAAkB,EAAE,GAAG;QACvB,kBAAkB,EAAE,QAAQ;KAC5B;IACD,YAAY,EAAE;QACb,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,kBAAkB;QAC7B,WAAW,EAAE,IAAI;QACjB,SAAS,EAAE;YACV,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,kBAAkB;YAC7B,UAAU,EAAE,gBAAgB;SAC5B;KACD;IACD,OAAO,EAAE;QACR,IAAI,EAAE,eAAe;QACrB,UAAU,EAAE,YAAY;QACxB,MAAM,EAAE,EAAyD;QACjE,YAAY,EAAE,oBAAoB;QAClC,UAAU,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,UAAU;QACzC,cAAc,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,UAAU;QAC7C,MAAM,EAAE,cAAc;QACtB,QAAQ,EAAE,mBAAmB;KAC7B;IACD,SAAS,EAAE;QACV,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,EAAgB;QAC5B,YAAY,EAAE,EAAiB;KAC/B;IACD,WAAW,EAAE;QACZ,UAAU,EAAE,QAAQ;QACpB,aAAa,EAAE,YAAY;QAC3B,aAAa,EAAE,eAAe;KAC9B;IACD,MAAM,EAAE;QACP;YACC,KAAK,EAAE;gBACN,IAAI,EAAE,kBAAkB;aACxB;YACD,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,EAAa;SACtB;QACD;YACC,KAAK,EAAE;gBACN,IAAI,EAAE,KAAK;aACX;YACD,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,EAAa;SACtB;KACD;IACD,SAAS,EAAE;QACV,gCAAgC,EAAE,eAAe;QACjD,eAAe,EAAE;YAChB,OAAO,EAAE,IAAI;SACb;QACD,iBAAiB,EAAE;YAClB,OAAO,EAAE,IAAI;YACb,gBAAgB,EAAE,IAAI;SACtB;QACD,oBAAoB,EAAE,IAAI;QAC1B,eAAe,EAAE;YAChB,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,KAAK;YACf,SAAS,EAAE,CAAC;SACZ;KACD;CACD,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ProjectConfig } from '../types';
2
+ export declare class Gateway {
3
+ private env;
4
+ private cfg;
5
+ private auth;
6
+ private strat;
7
+ private matcher;
8
+ constructor(env: Env, cfg: ProjectConfig);
9
+ fetch(request: Request): Promise<Response>;
10
+ }
11
+ //# sourceMappingURL=gateway.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/core/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAmB,MAAM,UAAU,CAAC;AAU/D,qBAAa,OAAO;IAMlB,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,GAAG;IANZ,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,OAAO,CAAe;gBAGrB,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,aAAa;IAQrB,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;CAuFhD"}
@@ -0,0 +1,97 @@
1
+ import { RouteMatcher } from '../routing/routeMatcher';
2
+ import { makeSessionStrategy } from '../sessions';
3
+ import { attachSignedUser, stripUser } from '../utils/propagation';
4
+ import { AuthRouter } from '../auth';
5
+ import { makeUserStore } from '../stores';
6
+ import { hasAllRoles, hasAnyRole } from '../utils/roles';
7
+ import { safeReturnTo } from '../utils/returnTo';
8
+ import { STATIC_ASSET_RE } from '../utils/helpers';
9
+ export class Gateway {
10
+ env;
11
+ cfg;
12
+ auth;
13
+ strat;
14
+ matcher;
15
+ constructor(env, cfg) {
16
+ this.env = env;
17
+ this.cfg = cfg;
18
+ const store = makeUserStore(cfg.userStore);
19
+ this.strat = makeSessionStrategy(cfg.session);
20
+ this.auth = new AuthRouter(cfg, env, store, this.strat);
21
+ this.matcher = new RouteMatcher(cfg.routes);
22
+ }
23
+ async fetch(request) {
24
+ const url = new URL(request.url);
25
+ // If the request is for auth, delegate to AuthRouter
26
+ if (/^\/auth(\/|$)/.test(url.pathname)) {
27
+ return this.auth.handle(request);
28
+ }
29
+ // Match route
30
+ const rule = this.matcher.match(url, request.method);
31
+ if (!rule) {
32
+ return new Response('Route not configured', { status: 501 });
33
+ }
34
+ // Check for existing session
35
+ const { session, accessJwt } = await this.strat.resolve(request, this.env);
36
+ // Common static asset extensions (tight allow-list)
37
+ const staticAssetRegex = this.cfg.overrides?.staticAssetRegex || STATIC_ASSET_RE;
38
+ const isStaticAsset = staticAssetRegex.test(url.pathname);
39
+ // Enforce auth if route requires it
40
+ if (rule.auth === 'required' && !(isStaticAsset && rule.bypassAuthForStaticAssets)) {
41
+ if (!this.auth.authFeatureEnabled()) {
42
+ return new Response('Authentication is disabled', { status: 501 });
43
+ }
44
+ if (!session) {
45
+ const returnTo = safeReturnTo(request.url, this.cfg.publicBaseUrl);
46
+ return this.auth.createUnauthenticatedRedirect(request.url, returnTo, rule.unauthenticatedRedirectUrl);
47
+ }
48
+ if (rule.requireRolesAll && !hasAllRoles(session.systemRoles, rule.requireRolesAll)) {
49
+ return new Response('Forbidden', { status: 403 });
50
+ }
51
+ if (rule.requireRolesAny && !hasAnyRole(session.systemRoles, rule.requireRolesAny)) {
52
+ return new Response('Forbidden', { status: 403 });
53
+ }
54
+ }
55
+ // Prepare forwarded request with appropriate headers
56
+ const headers = new Headers(request.headers);
57
+ if (session)
58
+ await attachSignedUser(headers, session, this.cfg, this.env);
59
+ else
60
+ stripUser(headers, this.cfg);
61
+ if (accessJwt)
62
+ headers.set('X-Access-Token', accessJwt);
63
+ // Get target service binding
64
+ const target = rule.service;
65
+ if (!target) {
66
+ return new Response(`Bad route: service binding not available`, { status: 502 });
67
+ }
68
+ const upstreamUrl = new URL(url.pathname + url.search, 'http://internal');
69
+ const method = request.method.toUpperCase();
70
+ const canHaveBody = method !== 'GET' && method !== 'HEAD';
71
+ // Clone headers
72
+ const fwdHeaders = new Headers(headers);
73
+ // Add standard forwarding headers
74
+ fwdHeaders.set('X-Forwarded-Host', url.host);
75
+ fwdHeaders.set('X-Forwarded-Proto', url.protocol.replace(':', ''));
76
+ fwdHeaders.set('X-Original-URL', url.pathname + url.search);
77
+ // Defensive: strip body-related headers for methods that cannot have a body
78
+ if (!canHaveBody) {
79
+ fwdHeaders.delete('content-length');
80
+ fwdHeaders.delete('transfer-encoding');
81
+ fwdHeaders.delete('content-type');
82
+ }
83
+ const init = {
84
+ method,
85
+ headers: fwdHeaders,
86
+ redirect: 'manual',
87
+ };
88
+ // Only attach body (and duplex) for methods that can have one
89
+ if (canHaveBody) {
90
+ init.body = request.body;
91
+ init.duplex = 'half';
92
+ }
93
+ const fwdReq = new Request(upstreamUrl, init);
94
+ return target.fetch(fwdReq);
95
+ }
96
+ }
97
+ //# sourceMappingURL=gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../src/core/gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { DurableObject } from 'cloudflare:workers';
2
+ export declare class SessionDO extends DurableObject {
3
+ private cache?;
4
+ constructor(ctx: DurableObjectState, env: Env);
5
+ fetch(req: Request): Promise<Response>;
6
+ alarm(): Promise<void>;
7
+ private isExpired;
8
+ private scheduleAlarm;
9
+ private deleteSession;
10
+ }
11
+ //# sourceMappingURL=sessionDo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionDo.d.ts","sourceRoot":"","sources":["../../src/do/sessionDo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAUnD,qBAAa,SAAU,SAAQ,aAAa;IAC3C,OAAO,CAAC,KAAK,CAAC,CAAW;gBAEb,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,GAAG;IAavC,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAmEtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B,OAAO,CAAC,SAAS;YAKH,aAAa;YAQb,aAAa;CAK3B"}
@@ -0,0 +1,96 @@
1
+ import { DurableObject } from 'cloudflare:workers';
2
+ import { json } from '../utils/http';
3
+ export class SessionDO extends DurableObject {
4
+ cache;
5
+ constructor(ctx, env) {
6
+ super(ctx, env);
7
+ this.ctx.blockConcurrencyWhile(async () => {
8
+ const s = await this.ctx.storage.get('session');
9
+ if (s && this.isExpired(s)) {
10
+ await this.deleteSession();
11
+ }
12
+ else if (s) {
13
+ this.cache = s;
14
+ await this.scheduleAlarm(s);
15
+ }
16
+ });
17
+ }
18
+ async fetch(req) {
19
+ if (req.method !== 'POST')
20
+ return new Response('method not allowed', { status: 405 });
21
+ const ct = req.headers.get('content-type') || '';
22
+ if (!ct.includes('application/json')) {
23
+ return json({ error: 'bad content-type' }, { status: 400 });
24
+ }
25
+ const { op, session, idleTtlSec, absoluteTtlSec } = (await req.json().catch(() => ({})));
26
+ if (op === 'put') {
27
+ if (!session?.userId || !session?.email)
28
+ return json({ error: 'invalid session: missing userId/email' }, { status: 400 });
29
+ const now = Date.now();
30
+ const idleMs = (idleTtlSec ?? 14 * 24 * 60 * 60) * 1000;
31
+ const absMs = (absoluteTtlSec ?? 30 * 24 * 60 * 60) * 1000;
32
+ const stored = {
33
+ ...session,
34
+ createdAt: now,
35
+ updatedAt: now,
36
+ idleMs,
37
+ expiresAt: now + idleMs,
38
+ absoluteAt: now + absMs,
39
+ };
40
+ this.cache = stored;
41
+ await this.ctx.storage.put('session', stored);
42
+ await this.ctx.storage.setAlarm(new Date(Math.min(stored.expiresAt, stored.absoluteAt)));
43
+ return json({ ok: true });
44
+ }
45
+ if (op === 'get') {
46
+ if (!this.cache) {
47
+ this.cache = (await this.ctx.storage.get('session')) || undefined;
48
+ }
49
+ if (!this.cache)
50
+ return json({ session: null });
51
+ if (this.isExpired(this.cache)) {
52
+ await this.deleteSession();
53
+ return json({ session: null });
54
+ }
55
+ const now = Date.now();
56
+ this.cache.updatedAt = now;
57
+ this.cache.expiresAt = Math.min(now + this.cache.idleMs, this.cache.absoluteAt);
58
+ await this.ctx.storage.put('session', this.cache);
59
+ await this.ctx.storage.setAlarm(new Date(Math.min(this.cache.expiresAt, this.cache.absoluteAt)));
60
+ const { expiresAt: _ex, absoluteAt: _ab, idleMs: _id, ...publicSession } = this.cache;
61
+ return json({ session: publicSession });
62
+ }
63
+ if (op === 'delete') {
64
+ await this.deleteSession();
65
+ return json({ ok: true });
66
+ }
67
+ return new Response('bad op', { status: 400 });
68
+ }
69
+ async alarm() {
70
+ const s = (await this.ctx.storage.get('session')) || undefined;
71
+ if (!s)
72
+ return;
73
+ if (this.isExpired(s)) {
74
+ await this.deleteSession();
75
+ return;
76
+ }
77
+ await this.ctx.storage.setAlarm(new Date(Math.min(s.expiresAt, s.absoluteAt)));
78
+ }
79
+ isExpired(s) {
80
+ const now = Date.now();
81
+ return now >= s.expiresAt || now >= s.absoluteAt;
82
+ }
83
+ async scheduleAlarm(s) {
84
+ if (!s) {
85
+ await this.ctx.storage.deleteAlarm?.();
86
+ return;
87
+ }
88
+ await this.ctx.storage.setAlarm(new Date(Math.min(s.expiresAt, s.absoluteAt)));
89
+ }
90
+ async deleteSession() {
91
+ this.cache = undefined;
92
+ await this.ctx.storage.delete('session');
93
+ await this.ctx.storage.deleteAlarm?.();
94
+ }
95
+ }
96
+ //# sourceMappingURL=sessionDo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionDo.js","sourceRoot":"","sources":["../../src/do/sessionDo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ProjectConfig } from './types';
2
+ export { SessionDO } from './do/sessionDo';
3
+ export default function createGateway(cfg: ProjectConfig): ExportedHandler<Env>;
4
+ export declare function defineConfig(config: ProjectConfig): ProjectConfig;
5
+ export type * from './types';
6
+ export * from './utils/verifyInternal';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAI3C,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,eAAe,CAAC,GAAG,CAAC,CAO9E;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAEjE;AAED,mBAAmB,SAAS,CAAC;AAC7B,cAAc,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ import { Gateway } from './core/gateway';
2
+ export { SessionDO } from './do/sessionDo';
3
+ let gateway;
4
+ export default function createGateway(cfg) {
5
+ return {
6
+ async fetch(request, env, _ctx) {
7
+ gateway ??= new Gateway(env, cfg);
8
+ return gateway.fetch(request);
9
+ },
10
+ };
11
+ }
12
+ export function defineConfig(config) {
13
+ return config;
14
+ }
15
+ export * from './utils/verifyInternal';
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAGzC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,IAAI,OAA4B,CAAC;AAEjC,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,GAAkB;IACvD,OAAO;QACN,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI;YAC7B,OAAO,KAAK,IAAI,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAClC,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;KACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAqB;IACjD,OAAO,MAAM,CAAC;AACf,CAAC;AAGD,cAAc,wBAAwB,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { ProviderConfig, LoginProviderId, ProviderIdentity, ProviderStatic, NormalizedClaims } from '../types';
2
+ export declare abstract class AuthProvider {
3
+ readonly id: LoginProviderId;
4
+ private readonly authorizeEndpoint;
5
+ private readonly tokenEndpoint;
6
+ private readonly defaultIssuer;
7
+ private readonly defaultScope?;
8
+ private readonly userInfoEndpoint?;
9
+ private readonly claimsMode;
10
+ constructor(staticCfg: ProviderStatic);
11
+ protected abstract normalize(claims: unknown): NormalizedClaims;
12
+ protected getDefaultScope(cfg: ProviderConfig): string;
13
+ protected getRedirectUri(baseUrl: string): string;
14
+ protected getClientSecret(env: Env, cfg: ProviderConfig): string | undefined;
15
+ loginURL(cfg: ProviderConfig, baseUrl: string, state: string, codeChallenge: string): string;
16
+ exchangeCode(cfg: ProviderConfig, env: Env, code: string, codeVerifier: string, redirectUri: string): Promise<ProviderIdentity>;
17
+ protected fetchUserInfo(userInfoEndpoint: string, accessToken: string): Promise<{
18
+ claims: unknown;
19
+ }>;
20
+ protected parseJwt(token: string): unknown;
21
+ }
22
+ //# sourceMappingURL=baseProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseProvider.d.ts","sourceRoot":"","sources":["../../src/providers/baseProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,cAAc,EACd,eAAe,EACf,gBAAgB,EAEhB,cAAc,EACd,gBAAgB,EAEhB,MAAM,UAAU,CAAC;AAElB,8BAAsB,YAAY;IACjC,QAAQ,CAAC,EAAE,EAAE,eAAe,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;gBAE5B,SAAS,EAAE,cAAc;IAUrC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB;IAE/D,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM;IAItD,SAAS,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAIjD,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS;IAK5E,QAAQ,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM;IAiBtF,YAAY,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;cAuErH,aAAa,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IAO1G,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CAW1C"}
@@ -0,0 +1,129 @@
1
+ export class AuthProvider {
2
+ id;
3
+ authorizeEndpoint;
4
+ tokenEndpoint;
5
+ defaultIssuer;
6
+ defaultScope;
7
+ userInfoEndpoint;
8
+ claimsMode;
9
+ constructor(staticCfg) {
10
+ this.id = staticCfg.id;
11
+ this.authorizeEndpoint = staticCfg.authorizeEndpoint;
12
+ this.tokenEndpoint = staticCfg.tokenEndpoint;
13
+ this.defaultIssuer = staticCfg.defaultIssuer;
14
+ this.defaultScope = staticCfg.defaultScope;
15
+ this.userInfoEndpoint = staticCfg.userInfoEndpoint;
16
+ this.claimsMode = staticCfg.claimsMode;
17
+ }
18
+ getDefaultScope(cfg) {
19
+ return cfg.scope ?? this.defaultScope ?? 'openid email profile';
20
+ }
21
+ getRedirectUri(baseUrl) {
22
+ return `${baseUrl}/auth/callback`;
23
+ }
24
+ getClientSecret(env, cfg) {
25
+ const key = cfg.clientSecretEnv;
26
+ return key ? env[key] : undefined;
27
+ }
28
+ loginURL(cfg, baseUrl, state, codeChallenge) {
29
+ const scope = this.getDefaultScope(cfg);
30
+ const redirectUri = this.getRedirectUri(baseUrl);
31
+ const qp = new URLSearchParams({
32
+ client_id: cfg.clientId,
33
+ response_type: 'code',
34
+ redirect_uri: redirectUri,
35
+ scope,
36
+ code_challenge: codeChallenge,
37
+ code_challenge_method: 'S256',
38
+ state,
39
+ });
40
+ return `${this.authorizeEndpoint}?${qp.toString()}`;
41
+ }
42
+ async exchangeCode(cfg, env, code, codeVerifier, redirectUri) {
43
+ const scope = this.getDefaultScope(cfg);
44
+ const clientSecret = this.getClientSecret(env, cfg);
45
+ const body = new URLSearchParams({
46
+ client_id: cfg.clientId,
47
+ code,
48
+ code_verifier: codeVerifier,
49
+ grant_type: 'authorization_code',
50
+ redirect_uri: redirectUri,
51
+ scope,
52
+ });
53
+ if (clientSecret)
54
+ body.set('client_secret', clientSecret);
55
+ const res = await fetch(this.tokenEndpoint, {
56
+ method: 'POST',
57
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
58
+ body,
59
+ });
60
+ if (!res.ok) {
61
+ const text = await res.text().catch(() => '');
62
+ throw new Error(`token exchange failed: ${res.status} ${text}`);
63
+ }
64
+ const json = (await res.json());
65
+ let claims = {};
66
+ if (this.claimsMode === 'id_token') {
67
+ if (typeof json.id_token !== 'string')
68
+ throw new Error('id_token_missing');
69
+ const decoded = this.parseJwt(json.id_token);
70
+ claims = decoded;
71
+ const issuer = (cfg.issuer ?? this.defaultIssuer) || '';
72
+ // iss
73
+ if (typeof decoded.iss === 'string' && decoded.iss !== issuer) {
74
+ throw new Error('bad_issuer');
75
+ }
76
+ // aud (string or string[])
77
+ const aud = decoded.aud;
78
+ const okAud = (typeof aud === 'string' && aud === cfg.clientId) || (Array.isArray(aud) && aud.includes(cfg.clientId));
79
+ if (!okAud)
80
+ throw new Error('bad_audience');
81
+ // exp
82
+ const exp = decoded.exp;
83
+ const now = Math.floor(Date.now() / 1000);
84
+ if (typeof exp === 'number' && now >= exp)
85
+ throw new Error('id_token_expired');
86
+ }
87
+ else if (this.claimsMode === 'userinfo') {
88
+ if (!this.userInfoEndpoint || typeof json.access_token !== 'string') {
89
+ throw new Error('userinfo_unavailable');
90
+ }
91
+ const info = await this.fetchUserInfo(this.userInfoEndpoint, json.access_token);
92
+ claims = info.claims;
93
+ }
94
+ else {
95
+ throw new Error('claims_unavailable');
96
+ }
97
+ const norm = this.normalize(claims);
98
+ if (!norm.email)
99
+ throw new Error('email_required');
100
+ if (!norm.subject)
101
+ throw new Error('missing_subject');
102
+ const issuer = (cfg.issuer ?? this.defaultIssuer) || '';
103
+ return {
104
+ email: norm.email,
105
+ provider: this.id,
106
+ issuer,
107
+ subject: norm.subject,
108
+ };
109
+ }
110
+ async fetchUserInfo(userInfoEndpoint, accessToken) {
111
+ const r = await fetch(userInfoEndpoint, { headers: { authorization: `Bearer ${accessToken}` } });
112
+ if (!r.ok)
113
+ throw new Error(`userinfo failed: ${r.status}`);
114
+ const claims = (await r.json());
115
+ return { claims };
116
+ }
117
+ parseJwt(token) {
118
+ const [, p] = token.split('.');
119
+ if (!p)
120
+ return {};
121
+ // base64url -> base64 with padding
122
+ let b64 = p.replace(/-/g, '+').replace(/_/g, '/');
123
+ const pad = b64.length % 4;
124
+ if (pad)
125
+ b64 += '='.repeat(4 - pad);
126
+ return JSON.parse(atob(b64));
127
+ }
128
+ }
129
+ //# sourceMappingURL=baseProvider.js.map