@every-app/sdk 0.1.10 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cloudflare/server/gateway.d.ts.map +1 -1
- package/dist/cloudflare/server/gateway.js +4 -5
- package/dist/core/authenticatedFetch.d.ts.map +1 -1
- package/dist/core/authenticatedFetch.js +4 -0
- package/dist/core/sessionManager.d.ts +3 -0
- package/dist/core/sessionManager.d.ts.map +1 -1
- package/dist/core/sessionManager.js +44 -9
- package/dist/shared/bypassGatewayLocalOnly.d.ts +14 -0
- package/dist/shared/bypassGatewayLocalOnly.d.ts.map +1 -0
- package/dist/shared/bypassGatewayLocalOnly.js +41 -0
- package/dist/tanstack/EmbeddedAppProvider.js +2 -2
- package/dist/tanstack/_internal/useEveryAppSession.d.ts.map +1 -1
- package/dist/tanstack/_internal/useEveryAppSession.js +2 -2
- package/dist/tanstack/server/authConfig.d.ts.map +1 -1
- package/dist/tanstack/server/authConfig.js +7 -1
- package/dist/tanstack/server/authenticateRequest.d.ts.map +1 -1
- package/dist/tanstack/server/authenticateRequest.js +11 -0
- package/dist/tanstack/useSessionTokenClientMiddleware.d.ts.map +1 -1
- package/dist/tanstack/useSessionTokenClientMiddleware.js +8 -0
- package/package.json +1 -4
- package/src/cloudflare/server/gateway.ts +4 -5
- package/src/core/authenticatedFetch.ts +9 -0
- package/src/core/sessionManager.ts +56 -8
- package/src/shared/bypassGatewayLocalOnly.ts +55 -0
- package/src/tanstack/EmbeddedAppProvider.tsx +2 -2
- package/src/tanstack/_internal/useEveryAppSession.tsx +3 -2
- package/src/tanstack/server/authConfig.ts +11 -1
- package/src/tanstack/server/authenticateRequest.test.ts +32 -0
- package/src/tanstack/server/authenticateRequest.ts +21 -0
- package/src/tanstack/useSessionTokenClientMiddleware.ts +12 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../../src/cloudflare/server/gateway.ts"],"names":[],"mappings":"AAGA,UAAU,cAAc;IACtB,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACxE;AAED,UAAU,UAAU;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,cAAc,CAAC;IACnC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,mBAAmB;IAC3B,GAAG,EAAE,UAAU,CAAC;IAChB;8DAC0D;IAC1D,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC;IAC5B,kFAAkF;IAClF,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAMrD;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,EACjC,GAAG,EACH,GAAG,EACH,IAAI,GACL,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../../src/cloudflare/server/gateway.ts"],"names":[],"mappings":"AAGA,UAAU,cAAc;IACtB,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACxE;AAED,UAAU,UAAU;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,cAAc,CAAC;IACnC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,mBAAmB;IAC3B,GAAG,EAAE,UAAU,CAAC;IAChB;8DAC0D;IAC1D,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC;IAC5B,kFAAkF;IAClF,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAMrD;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,EACjC,GAAG,EACH,GAAG,EACH,IAAI,GACL,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgBzC"}
|
|
@@ -19,16 +19,15 @@ export async function fetchGateway({ env, url, init, }) {
|
|
|
19
19
|
const gatewayBaseUrl = getGatewayUrl(env);
|
|
20
20
|
const resolvedRequest = toRequest(url, init, gatewayBaseUrl);
|
|
21
21
|
const authenticatedRequest = applyAppTokenAuth(resolvedRequest, env);
|
|
22
|
-
// Use service binding
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
if (import.meta.env.PROD && env.EVERY_APP_GATEWAY) {
|
|
22
|
+
// Use service binding when available for zero-latency internal routing
|
|
23
|
+
// (available in production Workers, not in local dev)
|
|
24
|
+
if (env.EVERY_APP_GATEWAY) {
|
|
26
25
|
const url = new URL(authenticatedRequest.url);
|
|
27
26
|
const bindingUrl = `${SERVICE_BINDING_ORIGIN}${url.pathname}${url.search}`;
|
|
28
27
|
const bindingRequest = new Request(bindingUrl, authenticatedRequest);
|
|
29
28
|
return env.EVERY_APP_GATEWAY.fetch(bindingRequest);
|
|
30
29
|
}
|
|
31
|
-
// HTTP fetch
|
|
30
|
+
// Fall back to HTTP fetch for local dev
|
|
32
31
|
return fetch(authenticatedRequest);
|
|
33
32
|
}
|
|
34
33
|
function applyAppTokenAuth(request, env) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authenticatedFetch.d.ts","sourceRoot":"","sources":["../../src/core/authenticatedFetch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"authenticatedFetch.d.ts","sourceRoot":"","sources":["../../src/core/authenticatedFetch.ts"],"names":[],"mappings":"AAaA;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAmBvD;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,WAAW,GAAG,GAAG,EACxB,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,QAAQ,CAAC,CAUnB"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { BYPASS_GATEWAY_LOCAL_ONLY_TOKEN, isBypassGatewayLocalOnlyClient, } from "../shared/bypassGatewayLocalOnly.js";
|
|
1
2
|
/**
|
|
2
3
|
* Gets the current session token from the embedded session manager
|
|
3
4
|
*/
|
|
4
5
|
export async function getSessionToken() {
|
|
6
|
+
if (isBypassGatewayLocalOnlyClient()) {
|
|
7
|
+
return BYPASS_GATEWAY_LOCAL_ONLY_TOKEN;
|
|
8
|
+
}
|
|
5
9
|
const windowWithSession = window;
|
|
6
10
|
const sessionManager = windowWithSession.__embeddedSessionManager;
|
|
7
11
|
if (!sessionManager) {
|
|
@@ -10,6 +10,9 @@ export declare class SessionManager {
|
|
|
10
10
|
readonly parentOrigin: string;
|
|
11
11
|
readonly appId: string;
|
|
12
12
|
readonly isInIframe: boolean;
|
|
13
|
+
readonly isBypassGatewayLocalOnly: boolean;
|
|
14
|
+
/** @deprecated Use isBypassGatewayLocalOnly instead. */
|
|
15
|
+
readonly isDemoModeLocalOnly: boolean;
|
|
13
16
|
private token;
|
|
14
17
|
private refreshPromise;
|
|
15
18
|
constructor(config: SessionManagerConfig);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionManager.d.ts","sourceRoot":"","sources":["../../src/core/sessionManager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sessionManager.d.ts","sourceRoot":"","sources":["../../src/core/sessionManager.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAQ3C;AAED,qBAAa,cAAc;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,wBAAwB,EAAE,OAAO,CAAC;IAC3C,wDAAwD;IACxD,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IAEtC,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,cAAc,CAAgC;gBAE1C,MAAM,EAAE,oBAAoB;IAqCxC,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,uBAAuB;IAuCzB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;IAsDlC,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAcjC,aAAa,IAAI;QACf,MAAM,EAAE,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,YAAY,CAAC;QACxD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;KACtB;IAgBD;;;OAGG;IACH,OAAO,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;CA+BpD"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BYPASS_GATEWAY_LOCAL_ONLY_EMAIL, BYPASS_GATEWAY_LOCAL_ONLY_TOKEN, BYPASS_GATEWAY_LOCAL_ONLY_USER_ID, isBypassGatewayLocalOnlyClient, } from "../shared/bypassGatewayLocalOnly.js";
|
|
1
2
|
const MESSAGE_TIMEOUT_MS = 5000;
|
|
2
3
|
const TOKEN_EXPIRY_BUFFER_MS = 10000;
|
|
3
4
|
const DEFAULT_TOKEN_LIFETIME_MS = 60000;
|
|
@@ -19,25 +20,40 @@ export class SessionManager {
|
|
|
19
20
|
parentOrigin;
|
|
20
21
|
appId;
|
|
21
22
|
isInIframe;
|
|
23
|
+
isBypassGatewayLocalOnly;
|
|
24
|
+
/** @deprecated Use isBypassGatewayLocalOnly instead. */
|
|
25
|
+
isDemoModeLocalOnly;
|
|
22
26
|
token = null;
|
|
23
27
|
refreshPromise = null;
|
|
24
28
|
constructor(config) {
|
|
25
29
|
if (!config.appId) {
|
|
26
30
|
throw new Error("[SessionManager] appId is required.");
|
|
27
31
|
}
|
|
32
|
+
this.isBypassGatewayLocalOnly = isBypassGatewayLocalOnlyClient();
|
|
33
|
+
this.isDemoModeLocalOnly = this.isBypassGatewayLocalOnly;
|
|
28
34
|
const gatewayUrl = import.meta.env.VITE_GATEWAY_URL;
|
|
29
|
-
if (!
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
if (!this.isBypassGatewayLocalOnly) {
|
|
36
|
+
if (!gatewayUrl) {
|
|
37
|
+
throw new Error("[SessionManager] VITE_GATEWAY_URL env var is required.");
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
new URL(gatewayUrl);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw new Error(`[SessionManager] Invalid gateway URL: ${gatewayUrl}`);
|
|
44
|
+
}
|
|
37
45
|
}
|
|
38
46
|
this.appId = config.appId;
|
|
39
|
-
this.parentOrigin =
|
|
47
|
+
this.parentOrigin = this.isBypassGatewayLocalOnly
|
|
48
|
+
? window.location.origin
|
|
49
|
+
: gatewayUrl;
|
|
40
50
|
this.isInIframe = isRunningInIframe();
|
|
51
|
+
if (this.isBypassGatewayLocalOnly) {
|
|
52
|
+
this.token = {
|
|
53
|
+
token: BYPASS_GATEWAY_LOCAL_ONLY_TOKEN,
|
|
54
|
+
expiresAt: Date.now() + DEFAULT_TOKEN_LIFETIME_MS,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
41
57
|
}
|
|
42
58
|
isTokenExpiringSoon(bufferMs = TOKEN_EXPIRY_BUFFER_MS) {
|
|
43
59
|
if (!this.token)
|
|
@@ -77,6 +93,13 @@ export class SessionManager {
|
|
|
77
93
|
});
|
|
78
94
|
}
|
|
79
95
|
async requestNewToken() {
|
|
96
|
+
if (this.isBypassGatewayLocalOnly) {
|
|
97
|
+
this.token = {
|
|
98
|
+
token: BYPASS_GATEWAY_LOCAL_ONLY_TOKEN,
|
|
99
|
+
expiresAt: Date.now() + DEFAULT_TOKEN_LIFETIME_MS,
|
|
100
|
+
};
|
|
101
|
+
return this.token.token;
|
|
102
|
+
}
|
|
80
103
|
if (this.refreshPromise) {
|
|
81
104
|
return this.refreshPromise;
|
|
82
105
|
}
|
|
@@ -112,6 +135,12 @@ export class SessionManager {
|
|
|
112
135
|
}
|
|
113
136
|
}
|
|
114
137
|
async getToken() {
|
|
138
|
+
if (this.isBypassGatewayLocalOnly) {
|
|
139
|
+
if (!this.token || this.isTokenExpiringSoon()) {
|
|
140
|
+
return this.requestNewToken();
|
|
141
|
+
}
|
|
142
|
+
return this.token.token;
|
|
143
|
+
}
|
|
115
144
|
if (this.isTokenExpiringSoon()) {
|
|
116
145
|
return this.requestNewToken();
|
|
117
146
|
}
|
|
@@ -134,6 +163,12 @@ export class SessionManager {
|
|
|
134
163
|
* Returns null if no valid token is available.
|
|
135
164
|
*/
|
|
136
165
|
getUser() {
|
|
166
|
+
if (this.isBypassGatewayLocalOnly) {
|
|
167
|
+
return {
|
|
168
|
+
userId: BYPASS_GATEWAY_LOCAL_ONLY_USER_ID,
|
|
169
|
+
email: BYPASS_GATEWAY_LOCAL_ONLY_EMAIL,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
137
172
|
if (!this.token) {
|
|
138
173
|
return null;
|
|
139
174
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const BYPASS_GATEWAY_LOCAL_ONLY_TOKEN = "BYPASS_GATEWAY_LOCAL_ONLY";
|
|
2
|
+
export declare const BYPASS_GATEWAY_LOCAL_ONLY_USER_ID = "demo-local-user";
|
|
3
|
+
export declare const BYPASS_GATEWAY_LOCAL_ONLY_EMAIL = "demo-local-user@local";
|
|
4
|
+
export declare function isBypassGatewayLocalOnlyClient(): boolean;
|
|
5
|
+
export declare function isBypassGatewayLocalOnlyServer(): boolean;
|
|
6
|
+
export declare function createBypassGatewayLocalOnlySessionPayload(audience: string): {
|
|
7
|
+
sub: string;
|
|
8
|
+
email: string;
|
|
9
|
+
iss: string;
|
|
10
|
+
aud: string;
|
|
11
|
+
iat: number;
|
|
12
|
+
exp: number;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=bypassGatewayLocalOnly.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bypassGatewayLocalOnly.d.ts","sourceRoot":"","sources":["../../src/shared/bypassGatewayLocalOnly.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,+BAA+B,8BAA8B,CAAC;AAC3E,eAAO,MAAM,iCAAiC,oBAAoB,CAAC;AACnE,eAAO,MAAM,+BAA+B,0BAA0B,CAAC;AAEvE,wBAAgB,8BAA8B,IAAI,OAAO,CAYxD;AAED,wBAAgB,8BAA8B,IAAI,OAAO,CAsBxD;AAED,wBAAgB,0CAA0C,CAAC,QAAQ,EAAE,MAAM;;;;;;;EAY1E"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const BYPASS_GATEWAY_LOCAL_ONLY_TOKEN = "BYPASS_GATEWAY_LOCAL_ONLY";
|
|
2
|
+
export const BYPASS_GATEWAY_LOCAL_ONLY_USER_ID = "demo-local-user";
|
|
3
|
+
export const BYPASS_GATEWAY_LOCAL_ONLY_EMAIL = "demo-local-user@local";
|
|
4
|
+
export function isBypassGatewayLocalOnlyClient() {
|
|
5
|
+
if (import.meta.env.PROD) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
const metaEnv = import.meta
|
|
9
|
+
.env;
|
|
10
|
+
return (metaEnv?.VITE_BYPASS_GATEWAY_LOCAL_ONLY === "true" ||
|
|
11
|
+
metaEnv?.BYPASS_GATEWAY_LOCAL_ONLY === "true");
|
|
12
|
+
}
|
|
13
|
+
export function isBypassGatewayLocalOnlyServer() {
|
|
14
|
+
if (import.meta.env.PROD) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const metaEnv = import.meta
|
|
18
|
+
.env;
|
|
19
|
+
const metaValue = metaEnv?.BYPASS_GATEWAY_LOCAL_ONLY ??
|
|
20
|
+
metaEnv?.VITE_BYPASS_GATEWAY_LOCAL_ONLY;
|
|
21
|
+
if (metaValue === "true") {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (typeof process !== "undefined" &&
|
|
25
|
+
process.env?.BYPASS_GATEWAY_LOCAL_ONLY === "true") {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
export function createBypassGatewayLocalOnlySessionPayload(audience) {
|
|
31
|
+
const issuedAt = Math.floor(Date.now() / 1000);
|
|
32
|
+
const expiresAt = issuedAt + 60 * 60;
|
|
33
|
+
return {
|
|
34
|
+
sub: BYPASS_GATEWAY_LOCAL_ONLY_USER_ID,
|
|
35
|
+
email: BYPASS_GATEWAY_LOCAL_ONLY_EMAIL,
|
|
36
|
+
iss: "local",
|
|
37
|
+
aud: audience,
|
|
38
|
+
iat: issuedAt,
|
|
39
|
+
exp: expiresAt,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -11,8 +11,8 @@ export function EmbeddedAppProvider({ children, ...config }) {
|
|
|
11
11
|
useEveryAppRouter({ sessionManager });
|
|
12
12
|
if (!sessionManager)
|
|
13
13
|
return null;
|
|
14
|
-
// Check if the app is running outside of the Gateway iframe
|
|
15
|
-
if (!sessionManager.isInIframe) {
|
|
14
|
+
// Check if the app is running outside of the Gateway iframe (skip in demo mode)
|
|
15
|
+
if (!sessionManager.isInIframe && !sessionManager.isBypassGatewayLocalOnly) {
|
|
16
16
|
return (_jsx(GatewayRequiredError, { gatewayOrigin: sessionManager.parentOrigin, appId: config.appId }));
|
|
17
17
|
}
|
|
18
18
|
const value = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEveryAppSession.d.ts","sourceRoot":"","sources":["../../../src/tanstack/_internal/useEveryAppSession.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,UAAU,oBAAoB;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,wBAAwB;IAChC,oBAAoB,EAAE,oBAAoB,CAAC;CAC5C;AAED,wBAAgB,kBAAkB,CAAC,EACjC,oBAAoB,GACrB,EAAE,wBAAwB;;;;;;
|
|
1
|
+
{"version":3,"file":"useEveryAppSession.d.ts","sourceRoot":"","sources":["../../../src/tanstack/_internal/useEveryAppSession.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,UAAU,oBAAoB;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,wBAAwB;IAChC,oBAAoB,EAAE,oBAAoB,CAAC;CAC5C;AAED,wBAAgB,kBAAkB,CAAC,EACjC,oBAAoB,GACrB,EAAE,wBAAwB;;;;;;EA+C1B"}
|
|
@@ -13,8 +13,8 @@ export function useEveryAppSession({ sessionManagerConfig, }) {
|
|
|
13
13
|
useEffect(() => {
|
|
14
14
|
if (!sessionManager)
|
|
15
15
|
return;
|
|
16
|
-
// Skip token requests when not in iframe - the app will show GatewayRequiredError instead
|
|
17
|
-
if (!sessionManager.isInIframe)
|
|
16
|
+
// Skip token requests when not in iframe (unless in demo mode) - the app will show GatewayRequiredError instead
|
|
17
|
+
if (!sessionManager.isInIframe && !sessionManager.isBypassGatewayLocalOnly)
|
|
18
18
|
return;
|
|
19
19
|
const interval = setInterval(() => {
|
|
20
20
|
setSessionTokenState(sessionManager.getTokenState());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authConfig.d.ts","sourceRoot":"","sources":["../../../src/tanstack/server/authConfig.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"authConfig.d.ts","sourceRoot":"","sources":["../../../src/tanstack/server/authConfig.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,wBAAgB,aAAa,IAAI,UAAU,CAc1C"}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { env } from "cloudflare:workers";
|
|
2
|
+
import { isBypassGatewayLocalOnlyServer } from "../../shared/bypassGatewayLocalOnly.js";
|
|
2
3
|
export function getAuthConfig() {
|
|
4
|
+
const bypassGatewayLocalOnlyEnv = env.BYPASS_GATEWAY_LOCAL_ONLY;
|
|
5
|
+
const isBypassGatewayLocalOnly = import.meta.env.PROD !== true &&
|
|
6
|
+
(bypassGatewayLocalOnlyEnv === "true" ||
|
|
7
|
+
isBypassGatewayLocalOnlyServer() === true);
|
|
8
|
+
const issuer = env.GATEWAY_URL || (isBypassGatewayLocalOnly ? "local" : "");
|
|
3
9
|
return {
|
|
4
|
-
issuer
|
|
10
|
+
issuer,
|
|
5
11
|
audience: import.meta.env.VITE_APP_ID,
|
|
6
12
|
};
|
|
7
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authenticateRequest.d.ts","sourceRoot":"","sources":["../../../src/tanstack/server/authenticateRequest.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"authenticateRequest.d.ts","sourceRoot":"","sources":["../../../src/tanstack/server/authenticateRequest.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAQ7C;;;GAGG;AACH,UAAU,mBAAmB;IAC3B,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,GAAG,EAAE,MAAM,CAAC;IACZ,2BAA2B;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,0BAA0B;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,UAAU,EACtB,eAAe,CAAC,EAAE,OAAO,GACxB,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CA8CrC;AAyCD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAK3E"}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { getRequest } from "@tanstack/react-start/server";
|
|
2
2
|
import { createLocalJWKSet, jwtVerify, } from "jose";
|
|
3
3
|
import { env } from "cloudflare:workers";
|
|
4
|
+
import { BYPASS_GATEWAY_LOCAL_ONLY_TOKEN, createBypassGatewayLocalOnlySessionPayload, isBypassGatewayLocalOnlyServer, } from "../../shared/bypassGatewayLocalOnly.js";
|
|
4
5
|
export async function authenticateRequest(authConfig, providedRequest) {
|
|
5
6
|
const request = providedRequest || getRequest();
|
|
6
7
|
const authHeader = request.headers.get("authorization");
|
|
8
|
+
const bypassGatewayLocalOnlyEnv = env.BYPASS_GATEWAY_LOCAL_ONLY;
|
|
9
|
+
const isBypassGatewayLocalOnly = import.meta.env.PROD !== true &&
|
|
10
|
+
(bypassGatewayLocalOnlyEnv === "true" ||
|
|
11
|
+
isBypassGatewayLocalOnlyServer() === true);
|
|
7
12
|
if (!authHeader) {
|
|
8
13
|
return null;
|
|
9
14
|
}
|
|
@@ -11,6 +16,12 @@ export async function authenticateRequest(authConfig, providedRequest) {
|
|
|
11
16
|
if (!token) {
|
|
12
17
|
return null;
|
|
13
18
|
}
|
|
19
|
+
if (isBypassGatewayLocalOnly) {
|
|
20
|
+
if (token !== BYPASS_GATEWAY_LOCAL_ONLY_TOKEN) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return createBypassGatewayLocalOnlySessionPayload(authConfig.audience);
|
|
24
|
+
}
|
|
14
25
|
try {
|
|
15
26
|
const session = await verifySessionToken(token, authConfig);
|
|
16
27
|
return session;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSessionTokenClientMiddleware.d.ts","sourceRoot":"","sources":["../../src/tanstack/useSessionTokenClientMiddleware.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useSessionTokenClientMiddleware.d.ts","sourceRoot":"","sources":["../../src/tanstack/useSessionTokenClientMiddleware.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,+BAA+B,6GAmC1C,CAAC"}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { createMiddleware } from "@tanstack/react-start";
|
|
2
|
+
import { BYPASS_GATEWAY_LOCAL_ONLY_TOKEN, isBypassGatewayLocalOnlyClient, } from "../shared/bypassGatewayLocalOnly.js";
|
|
2
3
|
export const useSessionTokenClientMiddleware = createMiddleware({
|
|
3
4
|
type: "function",
|
|
4
5
|
}).client(async ({ next }) => {
|
|
6
|
+
if (isBypassGatewayLocalOnlyClient()) {
|
|
7
|
+
return next({
|
|
8
|
+
headers: {
|
|
9
|
+
Authorization: `Bearer ${BYPASS_GATEWAY_LOCAL_ONLY_TOKEN}`,
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
}
|
|
5
13
|
// Get the global sessionManager - this MUST be available for embedded apps
|
|
6
14
|
const sessionManager = window
|
|
7
15
|
.__embeddedSessionManager;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@every-app/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -30,10 +30,7 @@
|
|
|
30
30
|
"src"
|
|
31
31
|
],
|
|
32
32
|
"scripts": {
|
|
33
|
-
"postinstall": "tsc -p tsconfig.build.json",
|
|
34
33
|
"build": "tsc -p tsconfig.build.json",
|
|
35
|
-
"build:prepublish": "pnpm install --ignore-scripts && npm run types:check && npm run build",
|
|
36
|
-
"prepublishOnly": "npm run build:prepublish",
|
|
37
34
|
"types:check": "tsc --noEmit",
|
|
38
35
|
"format:check": "prettier --check .",
|
|
39
36
|
"format:write": "prettier . --write",
|
|
@@ -46,17 +46,16 @@ export async function fetchGateway({
|
|
|
46
46
|
const resolvedRequest = toRequest(url, init, gatewayBaseUrl);
|
|
47
47
|
const authenticatedRequest = applyAppTokenAuth(resolvedRequest, env);
|
|
48
48
|
|
|
49
|
-
// Use service binding
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
if (import.meta.env.PROD && env.EVERY_APP_GATEWAY) {
|
|
49
|
+
// Use service binding when available for zero-latency internal routing
|
|
50
|
+
// (available in production Workers, not in local dev)
|
|
51
|
+
if (env.EVERY_APP_GATEWAY) {
|
|
53
52
|
const url = new URL(authenticatedRequest.url);
|
|
54
53
|
const bindingUrl = `${SERVICE_BINDING_ORIGIN}${url.pathname}${url.search}`;
|
|
55
54
|
const bindingRequest = new Request(bindingUrl, authenticatedRequest);
|
|
56
55
|
return env.EVERY_APP_GATEWAY.fetch(bindingRequest);
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
// HTTP fetch
|
|
58
|
+
// Fall back to HTTP fetch for local dev
|
|
60
59
|
return fetch(authenticatedRequest);
|
|
61
60
|
}
|
|
62
61
|
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BYPASS_GATEWAY_LOCAL_ONLY_TOKEN,
|
|
3
|
+
isBypassGatewayLocalOnlyClient,
|
|
4
|
+
} from "../shared/bypassGatewayLocalOnly.js";
|
|
5
|
+
|
|
1
6
|
interface SessionManager {
|
|
2
7
|
getToken(): Promise<string>;
|
|
3
8
|
}
|
|
@@ -10,6 +15,10 @@ interface WindowWithSessionManager extends Window {
|
|
|
10
15
|
* Gets the current session token from the embedded session manager
|
|
11
16
|
*/
|
|
12
17
|
export async function getSessionToken(): Promise<string> {
|
|
18
|
+
if (isBypassGatewayLocalOnlyClient()) {
|
|
19
|
+
return BYPASS_GATEWAY_LOCAL_ONLY_TOKEN;
|
|
20
|
+
}
|
|
21
|
+
|
|
13
22
|
const windowWithSession = window as WindowWithSessionManager;
|
|
14
23
|
const sessionManager = windowWithSession.__embeddedSessionManager;
|
|
15
24
|
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BYPASS_GATEWAY_LOCAL_ONLY_EMAIL,
|
|
3
|
+
BYPASS_GATEWAY_LOCAL_ONLY_TOKEN,
|
|
4
|
+
BYPASS_GATEWAY_LOCAL_ONLY_USER_ID,
|
|
5
|
+
isBypassGatewayLocalOnlyClient,
|
|
6
|
+
} from "../shared/bypassGatewayLocalOnly.js";
|
|
7
|
+
|
|
1
8
|
interface SessionToken {
|
|
2
9
|
token: string;
|
|
3
10
|
expiresAt: number;
|
|
@@ -35,6 +42,9 @@ export class SessionManager {
|
|
|
35
42
|
readonly parentOrigin: string;
|
|
36
43
|
readonly appId: string;
|
|
37
44
|
readonly isInIframe: boolean;
|
|
45
|
+
readonly isBypassGatewayLocalOnly: boolean;
|
|
46
|
+
/** @deprecated Use isBypassGatewayLocalOnly instead. */
|
|
47
|
+
readonly isDemoModeLocalOnly: boolean;
|
|
38
48
|
|
|
39
49
|
private token: SessionToken | null = null;
|
|
40
50
|
private refreshPromise: Promise<string> | null = null;
|
|
@@ -44,20 +54,36 @@ export class SessionManager {
|
|
|
44
54
|
throw new Error("[SessionManager] appId is required.");
|
|
45
55
|
}
|
|
46
56
|
|
|
57
|
+
this.isBypassGatewayLocalOnly = isBypassGatewayLocalOnlyClient();
|
|
58
|
+
this.isDemoModeLocalOnly = this.isBypassGatewayLocalOnly;
|
|
59
|
+
|
|
47
60
|
const gatewayUrl = import.meta.env.VITE_GATEWAY_URL;
|
|
48
|
-
if (!
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
if (!this.isBypassGatewayLocalOnly) {
|
|
62
|
+
if (!gatewayUrl) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
"[SessionManager] VITE_GATEWAY_URL env var is required.",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
51
67
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
try {
|
|
69
|
+
new URL(gatewayUrl);
|
|
70
|
+
} catch {
|
|
71
|
+
throw new Error(`[SessionManager] Invalid gateway URL: ${gatewayUrl}`);
|
|
72
|
+
}
|
|
56
73
|
}
|
|
57
74
|
|
|
58
75
|
this.appId = config.appId;
|
|
59
|
-
this.parentOrigin =
|
|
76
|
+
this.parentOrigin = this.isBypassGatewayLocalOnly
|
|
77
|
+
? window.location.origin
|
|
78
|
+
: gatewayUrl;
|
|
60
79
|
this.isInIframe = isRunningInIframe();
|
|
80
|
+
|
|
81
|
+
if (this.isBypassGatewayLocalOnly) {
|
|
82
|
+
this.token = {
|
|
83
|
+
token: BYPASS_GATEWAY_LOCAL_ONLY_TOKEN,
|
|
84
|
+
expiresAt: Date.now() + DEFAULT_TOKEN_LIFETIME_MS,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
61
87
|
}
|
|
62
88
|
|
|
63
89
|
private isTokenExpiringSoon(
|
|
@@ -107,6 +133,14 @@ export class SessionManager {
|
|
|
107
133
|
}
|
|
108
134
|
|
|
109
135
|
async requestNewToken(): Promise<string> {
|
|
136
|
+
if (this.isBypassGatewayLocalOnly) {
|
|
137
|
+
this.token = {
|
|
138
|
+
token: BYPASS_GATEWAY_LOCAL_ONLY_TOKEN,
|
|
139
|
+
expiresAt: Date.now() + DEFAULT_TOKEN_LIFETIME_MS,
|
|
140
|
+
};
|
|
141
|
+
return this.token.token;
|
|
142
|
+
}
|
|
143
|
+
|
|
110
144
|
if (this.refreshPromise) {
|
|
111
145
|
return this.refreshPromise;
|
|
112
146
|
}
|
|
@@ -153,6 +187,13 @@ export class SessionManager {
|
|
|
153
187
|
}
|
|
154
188
|
|
|
155
189
|
async getToken(): Promise<string> {
|
|
190
|
+
if (this.isBypassGatewayLocalOnly) {
|
|
191
|
+
if (!this.token || this.isTokenExpiringSoon()) {
|
|
192
|
+
return this.requestNewToken();
|
|
193
|
+
}
|
|
194
|
+
return this.token.token;
|
|
195
|
+
}
|
|
196
|
+
|
|
156
197
|
if (this.isTokenExpiringSoon()) {
|
|
157
198
|
return this.requestNewToken();
|
|
158
199
|
}
|
|
@@ -183,6 +224,13 @@ export class SessionManager {
|
|
|
183
224
|
* Returns null if no valid token is available.
|
|
184
225
|
*/
|
|
185
226
|
getUser(): { userId: string; email: string } | null {
|
|
227
|
+
if (this.isBypassGatewayLocalOnly) {
|
|
228
|
+
return {
|
|
229
|
+
userId: BYPASS_GATEWAY_LOCAL_ONLY_USER_ID,
|
|
230
|
+
email: BYPASS_GATEWAY_LOCAL_ONLY_EMAIL,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
186
234
|
if (!this.token) {
|
|
187
235
|
return null;
|
|
188
236
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const BYPASS_GATEWAY_LOCAL_ONLY_TOKEN = "BYPASS_GATEWAY_LOCAL_ONLY";
|
|
2
|
+
export const BYPASS_GATEWAY_LOCAL_ONLY_USER_ID = "demo-local-user";
|
|
3
|
+
export const BYPASS_GATEWAY_LOCAL_ONLY_EMAIL = "demo-local-user@local";
|
|
4
|
+
|
|
5
|
+
export function isBypassGatewayLocalOnlyClient(): boolean {
|
|
6
|
+
if (import.meta.env.PROD) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const metaEnv = (import.meta as { env?: Record<string, string | undefined> })
|
|
11
|
+
.env;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
metaEnv?.VITE_BYPASS_GATEWAY_LOCAL_ONLY === "true" ||
|
|
15
|
+
metaEnv?.BYPASS_GATEWAY_LOCAL_ONLY === "true"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isBypassGatewayLocalOnlyServer(): boolean {
|
|
20
|
+
if (import.meta.env.PROD) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const metaEnv = (import.meta as { env?: Record<string, string | undefined> })
|
|
25
|
+
.env;
|
|
26
|
+
const metaValue =
|
|
27
|
+
metaEnv?.BYPASS_GATEWAY_LOCAL_ONLY ??
|
|
28
|
+
metaEnv?.VITE_BYPASS_GATEWAY_LOCAL_ONLY;
|
|
29
|
+
if (metaValue === "true") {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
typeof process !== "undefined" &&
|
|
35
|
+
process.env?.BYPASS_GATEWAY_LOCAL_ONLY === "true"
|
|
36
|
+
) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createBypassGatewayLocalOnlySessionPayload(audience: string) {
|
|
44
|
+
const issuedAt = Math.floor(Date.now() / 1000);
|
|
45
|
+
const expiresAt = issuedAt + 60 * 60;
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
sub: BYPASS_GATEWAY_LOCAL_ONLY_USER_ID,
|
|
49
|
+
email: BYPASS_GATEWAY_LOCAL_ONLY_EMAIL,
|
|
50
|
+
iss: "local",
|
|
51
|
+
aud: audience,
|
|
52
|
+
iat: issuedAt,
|
|
53
|
+
exp: expiresAt,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -30,8 +30,8 @@ export function EmbeddedAppProvider({
|
|
|
30
30
|
|
|
31
31
|
if (!sessionManager) return null;
|
|
32
32
|
|
|
33
|
-
// Check if the app is running outside of the Gateway iframe
|
|
34
|
-
if (!sessionManager.isInIframe) {
|
|
33
|
+
// Check if the app is running outside of the Gateway iframe (skip in demo mode)
|
|
34
|
+
if (!sessionManager.isInIframe && !sessionManager.isBypassGatewayLocalOnly) {
|
|
35
35
|
return (
|
|
36
36
|
<GatewayRequiredError
|
|
37
37
|
gatewayOrigin={sessionManager.parentOrigin}
|
|
@@ -28,8 +28,9 @@ export function useEveryAppSession({
|
|
|
28
28
|
|
|
29
29
|
useEffect(() => {
|
|
30
30
|
if (!sessionManager) return;
|
|
31
|
-
// Skip token requests when not in iframe - the app will show GatewayRequiredError instead
|
|
32
|
-
if (!sessionManager.isInIframe)
|
|
31
|
+
// Skip token requests when not in iframe (unless in demo mode) - the app will show GatewayRequiredError instead
|
|
32
|
+
if (!sessionManager.isInIframe && !sessionManager.isBypassGatewayLocalOnly)
|
|
33
|
+
return;
|
|
33
34
|
|
|
34
35
|
const interval = setInterval(() => {
|
|
35
36
|
setSessionTokenState(sessionManager.getTokenState());
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import type { AuthConfig } from "./types.js";
|
|
2
2
|
import { env } from "cloudflare:workers";
|
|
3
|
+
import { isBypassGatewayLocalOnlyServer } from "../../shared/bypassGatewayLocalOnly.js";
|
|
3
4
|
|
|
4
5
|
export function getAuthConfig(): AuthConfig {
|
|
6
|
+
const bypassGatewayLocalOnlyEnv = (
|
|
7
|
+
env as { BYPASS_GATEWAY_LOCAL_ONLY?: string }
|
|
8
|
+
).BYPASS_GATEWAY_LOCAL_ONLY;
|
|
9
|
+
const isBypassGatewayLocalOnly =
|
|
10
|
+
import.meta.env.PROD !== true &&
|
|
11
|
+
(bypassGatewayLocalOnlyEnv === "true" ||
|
|
12
|
+
isBypassGatewayLocalOnlyServer() === true);
|
|
13
|
+
const issuer = env.GATEWAY_URL || (isBypassGatewayLocalOnly ? "local" : "");
|
|
14
|
+
|
|
5
15
|
return {
|
|
6
|
-
issuer
|
|
16
|
+
issuer,
|
|
7
17
|
audience: import.meta.env.VITE_APP_ID,
|
|
8
18
|
};
|
|
9
19
|
}
|
|
@@ -16,6 +16,7 @@ vi.mock("@tanstack/react-start/server", () => ({
|
|
|
16
16
|
|
|
17
17
|
import { authenticateRequest } from "./authenticateRequest";
|
|
18
18
|
import type { AuthConfig } from "./types";
|
|
19
|
+
import { env } from "cloudflare:workers";
|
|
19
20
|
|
|
20
21
|
describe("authenticateRequest", () => {
|
|
21
22
|
let keyPair: Awaited<ReturnType<typeof generateKeyPair>>;
|
|
@@ -25,6 +26,9 @@ describe("authenticateRequest", () => {
|
|
|
25
26
|
issuer: "https://gateway.example.com",
|
|
26
27
|
audience: "test-app",
|
|
27
28
|
};
|
|
29
|
+
const workersEnv = env as unknown as {
|
|
30
|
+
BYPASS_GATEWAY_LOCAL_ONLY?: string;
|
|
31
|
+
};
|
|
28
32
|
|
|
29
33
|
beforeEach(async () => {
|
|
30
34
|
// Generate a fresh key pair for each test
|
|
@@ -56,6 +60,7 @@ describe("authenticateRequest", () => {
|
|
|
56
60
|
|
|
57
61
|
afterEach(() => {
|
|
58
62
|
vi.restoreAllMocks();
|
|
63
|
+
delete workersEnv.BYPASS_GATEWAY_LOCAL_ONLY;
|
|
59
64
|
});
|
|
60
65
|
|
|
61
66
|
async function createValidToken(overrides: Record<string, unknown> = {}) {
|
|
@@ -117,6 +122,33 @@ describe("authenticateRequest", () => {
|
|
|
117
122
|
});
|
|
118
123
|
});
|
|
119
124
|
|
|
125
|
+
describe("BYPASS_GATEWAY_LOCAL_ONLY mode", () => {
|
|
126
|
+
it("accepts the local bypass token when BYPASS_GATEWAY_LOCAL_ONLY is true", async () => {
|
|
127
|
+
workersEnv.BYPASS_GATEWAY_LOCAL_ONLY = "true";
|
|
128
|
+
const request = createRequest("Bearer BYPASS_GATEWAY_LOCAL_ONLY");
|
|
129
|
+
|
|
130
|
+
const result = await authenticateRequest(authConfig, request);
|
|
131
|
+
|
|
132
|
+
expect(result).toMatchObject({
|
|
133
|
+
sub: "demo-local-user",
|
|
134
|
+
email: "demo-local-user@local",
|
|
135
|
+
iss: "local",
|
|
136
|
+
aud: authConfig.audience,
|
|
137
|
+
});
|
|
138
|
+
expect(global.fetch).not.toHaveBeenCalled();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("rejects non-bypass tokens when BYPASS_GATEWAY_LOCAL_ONLY is true", async () => {
|
|
142
|
+
workersEnv.BYPASS_GATEWAY_LOCAL_ONLY = "true";
|
|
143
|
+
const request = createRequest("Bearer regular-token");
|
|
144
|
+
|
|
145
|
+
const result = await authenticateRequest(authConfig, request);
|
|
146
|
+
|
|
147
|
+
expect(result).toBeNull();
|
|
148
|
+
expect(global.fetch).not.toHaveBeenCalled();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
120
152
|
describe("valid token verification", () => {
|
|
121
153
|
it("returns payload for valid token with correct issuer and audience", async () => {
|
|
122
154
|
const token = await createValidToken();
|
|
@@ -8,6 +8,11 @@ import {
|
|
|
8
8
|
|
|
9
9
|
import type { AuthConfig } from "./types.js";
|
|
10
10
|
import { env } from "cloudflare:workers";
|
|
11
|
+
import {
|
|
12
|
+
BYPASS_GATEWAY_LOCAL_ONLY_TOKEN,
|
|
13
|
+
createBypassGatewayLocalOnlySessionPayload,
|
|
14
|
+
isBypassGatewayLocalOnlyServer,
|
|
15
|
+
} from "../../shared/bypassGatewayLocalOnly.js";
|
|
11
16
|
|
|
12
17
|
/**
|
|
13
18
|
* JWT payload structure for embedded app session tokens.
|
|
@@ -35,6 +40,14 @@ export async function authenticateRequest(
|
|
|
35
40
|
const request = providedRequest || getRequest();
|
|
36
41
|
const authHeader = request.headers.get("authorization");
|
|
37
42
|
|
|
43
|
+
const bypassGatewayLocalOnlyEnv = (
|
|
44
|
+
env as { BYPASS_GATEWAY_LOCAL_ONLY?: string }
|
|
45
|
+
).BYPASS_GATEWAY_LOCAL_ONLY;
|
|
46
|
+
const isBypassGatewayLocalOnly =
|
|
47
|
+
import.meta.env.PROD !== true &&
|
|
48
|
+
(bypassGatewayLocalOnlyEnv === "true" ||
|
|
49
|
+
isBypassGatewayLocalOnlyServer() === true);
|
|
50
|
+
|
|
38
51
|
if (!authHeader) {
|
|
39
52
|
return null;
|
|
40
53
|
}
|
|
@@ -45,6 +58,14 @@ export async function authenticateRequest(
|
|
|
45
58
|
return null;
|
|
46
59
|
}
|
|
47
60
|
|
|
61
|
+
if (isBypassGatewayLocalOnly) {
|
|
62
|
+
if (token !== BYPASS_GATEWAY_LOCAL_ONLY_TOKEN) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return createBypassGatewayLocalOnlySessionPayload(authConfig.audience);
|
|
67
|
+
}
|
|
68
|
+
|
|
48
69
|
try {
|
|
49
70
|
const session = await verifySessionToken(token, authConfig);
|
|
50
71
|
return session;
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import { createMiddleware } from "@tanstack/react-start";
|
|
2
2
|
import type { SessionManager } from "../core/sessionManager.js";
|
|
3
|
+
import {
|
|
4
|
+
BYPASS_GATEWAY_LOCAL_ONLY_TOKEN,
|
|
5
|
+
isBypassGatewayLocalOnlyClient,
|
|
6
|
+
} from "../shared/bypassGatewayLocalOnly.js";
|
|
3
7
|
|
|
4
8
|
export const useSessionTokenClientMiddleware = createMiddleware({
|
|
5
9
|
type: "function",
|
|
6
10
|
}).client(async ({ next }) => {
|
|
11
|
+
if (isBypassGatewayLocalOnlyClient()) {
|
|
12
|
+
return next({
|
|
13
|
+
headers: {
|
|
14
|
+
Authorization: `Bearer ${BYPASS_GATEWAY_LOCAL_ONLY_TOKEN}`,
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
// Get the global sessionManager - this MUST be available for embedded apps
|
|
8
20
|
const sessionManager = (window as any)
|
|
9
21
|
.__embeddedSessionManager as SessionManager;
|