@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.
- package/README.md +59 -0
- package/dist/auth/index.d.ts +84 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +609 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/pkceState.d.ts +40 -0
- package/dist/auth/pkceState.d.ts.map +1 -0
- package/dist/auth/pkceState.js +75 -0
- package/dist/auth/pkceState.js.map +1 -0
- package/dist/config.example.d.ts +2 -0
- package/dist/config.example.d.ts.map +1 -0
- package/dist/config.example.js +83 -0
- package/dist/config.example.js.map +1 -0
- package/dist/core/gateway.d.ts +11 -0
- package/dist/core/gateway.d.ts.map +1 -0
- package/dist/core/gateway.js +97 -0
- package/dist/core/gateway.js.map +1 -0
- package/dist/do/sessionDo.d.ts +11 -0
- package/dist/do/sessionDo.d.ts.map +1 -0
- package/dist/do/sessionDo.js +96 -0
- package/dist/do/sessionDo.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/baseProvider.d.ts +22 -0
- package/dist/providers/baseProvider.d.ts.map +1 -0
- package/dist/providers/baseProvider.js +129 -0
- package/dist/providers/baseProvider.js.map +1 -0
- package/dist/providers/google.d.ts +9 -0
- package/dist/providers/google.d.ts.map +1 -0
- package/dist/providers/google.js +27 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/index.d.ts +3 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +5 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/routing/routeMatcher.d.ts +15 -0
- package/dist/routing/routeMatcher.d.ts.map +1 -0
- package/dist/routing/routeMatcher.js +83 -0
- package/dist/routing/routeMatcher.js.map +1 -0
- package/dist/sessions/durableObjectSession.d.ts +25 -0
- package/dist/sessions/durableObjectSession.d.ts.map +1 -0
- package/dist/sessions/durableObjectSession.js +90 -0
- package/dist/sessions/durableObjectSession.js.map +1 -0
- package/dist/sessions/index.d.ts +19 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/dist/sessions/index.js +32 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/sessions/jwtSession.d.ts +19 -0
- package/dist/sessions/jwtSession.d.ts.map +1 -0
- package/dist/sessions/jwtSession.js +49 -0
- package/dist/sessions/jwtSession.js.map +1 -0
- package/dist/stores/index.d.ts +3 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/index.js +10 -0
- package/dist/stores/index.js.map +1 -0
- package/dist/stores/postgres.d.ts +74 -0
- package/dist/stores/postgres.d.ts.map +1 -0
- package/dist/stores/postgres.js +231 -0
- package/dist/stores/postgres.js.map +1 -0
- package/dist/types.d.ts +247 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/csrf.d.ts +13 -0
- package/dist/utils/csrf.d.ts.map +1 -0
- package/dist/utils/csrf.js +42 -0
- package/dist/utils/csrf.js.map +1 -0
- package/dist/utils/helpers.d.ts +8 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +22 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/http.d.ts +9 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +23 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/jwt.d.ts +22 -0
- package/dist/utils/jwt.d.ts.map +1 -0
- package/dist/utils/jwt.js +96 -0
- package/dist/utils/jwt.js.map +1 -0
- package/dist/utils/passwordPolicy.d.ts +9 -0
- package/dist/utils/passwordPolicy.d.ts.map +1 -0
- package/dist/utils/passwordPolicy.js +29 -0
- package/dist/utils/passwordPolicy.js.map +1 -0
- package/dist/utils/passwords.d.ts +33 -0
- package/dist/utils/passwords.d.ts.map +1 -0
- package/dist/utils/passwords.js +139 -0
- package/dist/utils/passwords.js.map +1 -0
- package/dist/utils/propagation.d.ts +30 -0
- package/dist/utils/propagation.d.ts.map +1 -0
- package/dist/utils/propagation.js +60 -0
- package/dist/utils/propagation.js.map +1 -0
- package/dist/utils/returnTo.d.ts +2 -0
- package/dist/utils/returnTo.d.ts.map +1 -0
- package/dist/utils/returnTo.js +21 -0
- package/dist/utils/returnTo.js.map +1 -0
- package/dist/utils/roles.d.ts +3 -0
- package/dist/utils/roles.d.ts.map +1 -0
- package/dist/utils/roles.js +25 -0
- package/dist/utils/roles.js.map +1 -0
- package/dist/utils/turnstile.d.ts +12 -0
- package/dist/utils/turnstile.d.ts.map +1 -0
- package/dist/utils/turnstile.js +40 -0
- package/dist/utils/turnstile.js.map +1 -0
- package/dist/utils/verifyInternal.d.ts +8 -0
- package/dist/utils/verifyInternal.d.ts.map +1 -0
- package/dist/utils/verifyInternal.js +69 -0
- package/dist/utils/verifyInternal.js.map +1 -0
- 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 @@
|
|
|
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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|