@every-app/sdk 0.0.6 → 0.0.7
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/client/EmbeddedAppProvider.d.ts +5 -8
- package/dist/client/EmbeddedAppProvider.d.ts.map +1 -1
- package/dist/client/EmbeddedAppProvider.js +28 -27
- package/dist/client/GatewayRequiredError.d.ts +20 -0
- package/dist/client/GatewayRequiredError.d.ts.map +1 -0
- package/dist/client/GatewayRequiredError.js +97 -0
- package/dist/client/_internal/useEveryAppRouter.d.ts +3 -5
- package/dist/client/_internal/useEveryAppRouter.js +53 -56
- package/dist/client/_internal/useEveryAppSession.d.ts +8 -10
- package/dist/client/_internal/useEveryAppSession.d.ts.map +1 -1
- package/dist/client/_internal/useEveryAppSession.js +32 -27
- package/dist/client/authenticatedFetch.d.ts +2 -5
- package/dist/client/authenticatedFetch.js +17 -17
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -1
- package/dist/client/lazyInitForWorkers.d.ts +2 -4
- package/dist/client/lazyInitForWorkers.js +42 -45
- package/dist/client/session-manager.d.ts +29 -23
- package/dist/client/session-manager.d.ts.map +1 -1
- package/dist/client/session-manager.js +143 -126
- package/dist/client/useSessionTokenClientMiddleware.d.ts +2 -8
- package/dist/client/useSessionTokenClientMiddleware.js +17 -20
- package/dist/server/auth-config.d.ts +1 -1
- package/dist/server/auth-config.js +4 -4
- package/dist/server/authenticateRequest.d.ts +11 -16
- package/dist/server/authenticateRequest.js +53 -57
- package/dist/server/getLocalD1Url.d.ts +1 -1
- package/dist/server/getLocalD1Url.js +29 -42
- package/dist/server/index.d.ts +1 -1
- package/dist/server/types.d.ts +3 -3
- package/package.json +2 -2
- package/src/client/EmbeddedAppProvider.tsx +11 -0
- package/src/client/GatewayRequiredError.tsx +161 -0
- package/src/client/_internal/useEveryAppSession.tsx +3 -0
- package/src/client/index.ts +2 -1
- package/src/client/session-manager.ts +16 -0
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
export interface SessionManagerConfig {
|
|
2
|
-
|
|
2
|
+
appId: string;
|
|
3
3
|
}
|
|
4
|
+
/**
|
|
5
|
+
* Detects whether the current window is running inside an iframe.
|
|
6
|
+
* Returns true if in an iframe, false if running as top-level window.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isRunningInIframe(): boolean;
|
|
4
9
|
export declare class SessionManager {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
10
|
+
readonly parentOrigin: string;
|
|
11
|
+
readonly appId: string;
|
|
12
|
+
readonly isInIframe: boolean;
|
|
13
|
+
private token;
|
|
14
|
+
private refreshPromise;
|
|
15
|
+
constructor(config: SessionManagerConfig);
|
|
16
|
+
private isTokenExpiringSoon;
|
|
17
|
+
private postMessageWithResponse;
|
|
18
|
+
requestNewToken(): Promise<string>;
|
|
19
|
+
getToken(): Promise<string>;
|
|
20
|
+
getTokenState(): {
|
|
21
|
+
status: "NO_TOKEN" | "VALID" | "EXPIRED" | "REFRESHING";
|
|
22
|
+
token: string | null;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Extracts user information from the current JWT token.
|
|
26
|
+
* Returns null if no valid token is available.
|
|
27
|
+
*/
|
|
28
|
+
getUser(): {
|
|
29
|
+
userId: string;
|
|
30
|
+
email: string;
|
|
31
|
+
} | null;
|
|
26
32
|
}
|
|
27
|
-
//# sourceMappingURL=session-manager.d.ts.map
|
|
33
|
+
//# sourceMappingURL=session-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/client/session-manager.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AAMD,qBAAa,cAAc;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/client/session-manager.ts"],"names":[],"mappings":"AAWA,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;IAE7B,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,cAAc,CAAgC;gBAE1C,MAAM,EAAE,oBAAoB;IAqBxC,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,uBAAuB;IAuCzB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;IA8ClC,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAOjC,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;CAwBpD"}
|
|
@@ -1,141 +1,158 @@
|
|
|
1
1
|
const MESSAGE_TIMEOUT_MS = 5000;
|
|
2
2
|
const TOKEN_EXPIRY_BUFFER_MS = 10000;
|
|
3
3
|
const DEFAULT_TOKEN_LIFETIME_MS = 60000;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
constructor(config) {
|
|
10
|
-
if (!config.appId) {
|
|
11
|
-
throw new Error("[SessionManager] appId is required.");
|
|
12
|
-
}
|
|
13
|
-
const gatewayUrl = import.meta.env.VITE_GATEWAY_URL;
|
|
14
|
-
if (!gatewayUrl) {
|
|
15
|
-
throw new Error("[SessionManager] VITE_GATEWAY_URL env var is required.");
|
|
16
|
-
}
|
|
4
|
+
/**
|
|
5
|
+
* Detects whether the current window is running inside an iframe.
|
|
6
|
+
* Returns true if in an iframe, false if running as top-level window.
|
|
7
|
+
*/
|
|
8
|
+
export function isRunningInIframe() {
|
|
17
9
|
try {
|
|
18
|
-
|
|
19
|
-
} catch {
|
|
20
|
-
throw new Error(`[SessionManager] Invalid gateway URL: ${gatewayUrl}`);
|
|
10
|
+
return window.self !== window.top;
|
|
21
11
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (!this.token) return true;
|
|
27
|
-
return Date.now() >= this.token.expiresAt - bufferMs;
|
|
28
|
-
}
|
|
29
|
-
postMessageWithResponse(request, responseType, requestId) {
|
|
30
|
-
return new Promise((resolve, reject) => {
|
|
31
|
-
const cleanup = () => {
|
|
32
|
-
clearTimeout(timeout);
|
|
33
|
-
window.removeEventListener("message", handler);
|
|
34
|
-
};
|
|
35
|
-
const handler = (event) => {
|
|
36
|
-
// Security: reject messages from wrong origin (including null from sandboxed iframes)
|
|
37
|
-
if (event.origin !== this.parentOrigin) return;
|
|
38
|
-
// Safety: ignore malformed messages that could crash the handler
|
|
39
|
-
if (!event.data || typeof event.data !== "object") return;
|
|
40
|
-
if (
|
|
41
|
-
event.data.type === responseType &&
|
|
42
|
-
event.data.requestId === requestId
|
|
43
|
-
) {
|
|
44
|
-
cleanup();
|
|
45
|
-
if (event.data.error) {
|
|
46
|
-
reject(new Error(event.data.error));
|
|
47
|
-
} else {
|
|
48
|
-
resolve(event.data);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
const timeout = setTimeout(() => {
|
|
53
|
-
cleanup();
|
|
54
|
-
reject(new Error("Token request timeout - parent did not respond"));
|
|
55
|
-
}, MESSAGE_TIMEOUT_MS);
|
|
56
|
-
window.addEventListener("message", handler);
|
|
57
|
-
window.parent.postMessage(request, this.parentOrigin);
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
async requestNewToken() {
|
|
61
|
-
if (this.refreshPromise) {
|
|
62
|
-
return this.refreshPromise;
|
|
12
|
+
catch {
|
|
13
|
+
// If we can't access window.top due to cross-origin restrictions,
|
|
14
|
+
// we're definitely in an iframe
|
|
15
|
+
return true;
|
|
63
16
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
);
|
|
75
|
-
if (!response.token) {
|
|
76
|
-
throw new Error("No token in response");
|
|
77
|
-
}
|
|
78
|
-
// Parse expiresAt, falling back to default lifetime if invalid
|
|
79
|
-
let expiresAt = Date.now() + DEFAULT_TOKEN_LIFETIME_MS;
|
|
80
|
-
if (response.expiresAt) {
|
|
81
|
-
const parsed = new Date(response.expiresAt).getTime();
|
|
82
|
-
if (!Number.isNaN(parsed)) {
|
|
83
|
-
expiresAt = parsed;
|
|
17
|
+
}
|
|
18
|
+
export class SessionManager {
|
|
19
|
+
parentOrigin;
|
|
20
|
+
appId;
|
|
21
|
+
isInIframe;
|
|
22
|
+
token = null;
|
|
23
|
+
refreshPromise = null;
|
|
24
|
+
constructor(config) {
|
|
25
|
+
if (!config.appId) {
|
|
26
|
+
throw new Error("[SessionManager] appId is required.");
|
|
84
27
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
28
|
+
const gatewayUrl = import.meta.env.VITE_GATEWAY_URL;
|
|
29
|
+
if (!gatewayUrl) {
|
|
30
|
+
throw new Error("[SessionManager] VITE_GATEWAY_URL env var is required.");
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
new URL(gatewayUrl);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
throw new Error(`[SessionManager] Invalid gateway URL: ${gatewayUrl}`);
|
|
37
|
+
}
|
|
38
|
+
this.appId = config.appId;
|
|
39
|
+
this.parentOrigin = gatewayUrl;
|
|
40
|
+
this.isInIframe = isRunningInIframe();
|
|
96
41
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
42
|
+
isTokenExpiringSoon(bufferMs = TOKEN_EXPIRY_BUFFER_MS) {
|
|
43
|
+
if (!this.token)
|
|
44
|
+
return true;
|
|
45
|
+
return Date.now() >= this.token.expiresAt - bufferMs;
|
|
101
46
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
47
|
+
postMessageWithResponse(request, responseType, requestId) {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
const cleanup = () => {
|
|
50
|
+
clearTimeout(timeout);
|
|
51
|
+
window.removeEventListener("message", handler);
|
|
52
|
+
};
|
|
53
|
+
const handler = (event) => {
|
|
54
|
+
// Security: reject messages from wrong origin (including null from sandboxed iframes)
|
|
55
|
+
if (event.origin !== this.parentOrigin)
|
|
56
|
+
return;
|
|
57
|
+
// Safety: ignore malformed messages that could crash the handler
|
|
58
|
+
if (!event.data || typeof event.data !== "object")
|
|
59
|
+
return;
|
|
60
|
+
if (event.data.type === responseType &&
|
|
61
|
+
event.data.requestId === requestId) {
|
|
62
|
+
cleanup();
|
|
63
|
+
if (event.data.error) {
|
|
64
|
+
reject(new Error(event.data.error));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
resolve(event.data);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const timeout = setTimeout(() => {
|
|
72
|
+
cleanup();
|
|
73
|
+
reject(new Error("Token request timeout - parent did not respond"));
|
|
74
|
+
}, MESSAGE_TIMEOUT_MS);
|
|
75
|
+
window.addEventListener("message", handler);
|
|
76
|
+
window.parent.postMessage(request, this.parentOrigin);
|
|
77
|
+
});
|
|
107
78
|
}
|
|
108
|
-
|
|
109
|
-
|
|
79
|
+
async requestNewToken() {
|
|
80
|
+
if (this.refreshPromise) {
|
|
81
|
+
return this.refreshPromise;
|
|
82
|
+
}
|
|
83
|
+
this.refreshPromise = (async () => {
|
|
84
|
+
const requestId = crypto.randomUUID();
|
|
85
|
+
const response = await this.postMessageWithResponse({
|
|
86
|
+
type: "SESSION_TOKEN_REQUEST",
|
|
87
|
+
requestId,
|
|
88
|
+
appId: this.appId,
|
|
89
|
+
}, "SESSION_TOKEN_RESPONSE", requestId);
|
|
90
|
+
if (!response.token) {
|
|
91
|
+
throw new Error("No token in response");
|
|
92
|
+
}
|
|
93
|
+
// Parse expiresAt, falling back to default lifetime if invalid
|
|
94
|
+
let expiresAt = Date.now() + DEFAULT_TOKEN_LIFETIME_MS;
|
|
95
|
+
if (response.expiresAt) {
|
|
96
|
+
const parsed = new Date(response.expiresAt).getTime();
|
|
97
|
+
if (!Number.isNaN(parsed)) {
|
|
98
|
+
expiresAt = parsed;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
this.token = {
|
|
102
|
+
token: response.token,
|
|
103
|
+
expiresAt,
|
|
104
|
+
};
|
|
105
|
+
return this.token.token;
|
|
106
|
+
})();
|
|
107
|
+
try {
|
|
108
|
+
return await this.refreshPromise;
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
this.refreshPromise = null;
|
|
112
|
+
}
|
|
110
113
|
}
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
async getToken() {
|
|
115
|
+
if (this.isTokenExpiringSoon()) {
|
|
116
|
+
return this.requestNewToken();
|
|
117
|
+
}
|
|
118
|
+
return this.token.token;
|
|
113
119
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
getTokenState() {
|
|
121
|
+
if (this.refreshPromise) {
|
|
122
|
+
return { status: "REFRESHING", token: null };
|
|
123
|
+
}
|
|
124
|
+
if (!this.token) {
|
|
125
|
+
return { status: "NO_TOKEN", token: null };
|
|
126
|
+
}
|
|
127
|
+
if (this.isTokenExpiringSoon(0)) {
|
|
128
|
+
return { status: "EXPIRED", token: this.token.token };
|
|
129
|
+
}
|
|
130
|
+
return { status: "VALID", token: this.token.token };
|
|
123
131
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
/**
|
|
133
|
+
* Extracts user information from the current JWT token.
|
|
134
|
+
* Returns null if no valid token is available.
|
|
135
|
+
*/
|
|
136
|
+
getUser() {
|
|
137
|
+
if (!this.token) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
const parts = this.token.token.split(".");
|
|
142
|
+
if (parts.length !== 3) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const payload = JSON.parse(atob(parts[1]));
|
|
146
|
+
if (!payload.sub) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
userId: payload.sub,
|
|
151
|
+
email: payload.email ?? "",
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
139
157
|
}
|
|
140
|
-
}
|
|
141
158
|
}
|
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
export declare const useSessionTokenClientMiddleware: import("@tanstack/react-start").FunctionMiddlewareAfterClient<
|
|
2
|
-
|
|
3
|
-
unknown,
|
|
4
|
-
undefined,
|
|
5
|
-
undefined,
|
|
6
|
-
undefined
|
|
7
|
-
>;
|
|
8
|
-
//# sourceMappingURL=useSessionTokenClientMiddleware.d.ts.map
|
|
1
|
+
export declare const useSessionTokenClientMiddleware: import("@tanstack/react-start").FunctionMiddlewareAfterClient<{}, unknown, undefined, undefined, undefined>;
|
|
2
|
+
//# sourceMappingURL=useSessionTokenClientMiddleware.d.ts.map
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import { createMiddleware } from "@tanstack/react-start";
|
|
2
2
|
export const useSessionTokenClientMiddleware = createMiddleware({
|
|
3
|
-
|
|
3
|
+
type: "function",
|
|
4
4
|
}).client(async ({ next }) => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Authorization: `Bearer ${token}`,
|
|
22
|
-
},
|
|
23
|
-
});
|
|
5
|
+
// Get the global sessionManager - this MUST be available for embedded apps
|
|
6
|
+
const sessionManager = window
|
|
7
|
+
.__embeddedSessionManager;
|
|
8
|
+
if (!sessionManager) {
|
|
9
|
+
throw new Error("[AuthMiddleware] SessionManager not available - embedded provider not initialized");
|
|
10
|
+
}
|
|
11
|
+
// INVARIANT: This is just an extra check and should never be the case if the sessionManager exists.
|
|
12
|
+
if (typeof sessionManager.getToken !== "function") {
|
|
13
|
+
throw new Error("[AuthMiddleware] SessionManager.getToken is not a function");
|
|
14
|
+
}
|
|
15
|
+
const token = await sessionManager.getToken();
|
|
16
|
+
return next({
|
|
17
|
+
headers: {
|
|
18
|
+
Authorization: `Bearer ${token}`,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
24
21
|
});
|
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
import type { AuthConfig } from "./types";
|
|
2
2
|
interface SessionTokenPayload {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
sub: string;
|
|
4
|
+
iss: string;
|
|
5
|
+
aud: string;
|
|
6
|
+
exp: number;
|
|
7
|
+
iat: number;
|
|
8
|
+
appId?: string;
|
|
9
|
+
permissions?: string[];
|
|
10
|
+
email?: string;
|
|
11
11
|
}
|
|
12
|
-
export declare function authenticateRequest(
|
|
13
|
-
authConfig: AuthConfig,
|
|
14
|
-
providedRequest?: Request,
|
|
15
|
-
): Promise<SessionTokenPayload | null>;
|
|
12
|
+
export declare function authenticateRequest(authConfig: AuthConfig, providedRequest?: Request): Promise<SessionTokenPayload | null>;
|
|
16
13
|
/**
|
|
17
14
|
* Extracts the bearer token from an Authorization header.
|
|
18
15
|
*
|
|
19
16
|
* @param authHeader - The Authorization header value (e.g., "Bearer eyJ...")
|
|
20
17
|
* @returns The token string if valid, null otherwise
|
|
21
18
|
*/
|
|
22
|
-
export declare function extractBearerToken(
|
|
23
|
-
authHeader: string | null,
|
|
24
|
-
): string | null;
|
|
19
|
+
export declare function extractBearerToken(authHeader: string | null): string | null;
|
|
25
20
|
export {};
|
|
26
|
-
//# sourceMappingURL=authenticateRequest.d.ts.map
|
|
21
|
+
//# sourceMappingURL=authenticateRequest.d.ts.map
|
|
@@ -1,61 +1,57 @@
|
|
|
1
1
|
import { getRequest } from "@tanstack/react-start/server";
|
|
2
|
-
import { createLocalJWKSet, jwtVerify } from "jose";
|
|
2
|
+
import { createLocalJWKSet, jwtVerify, } from "jose";
|
|
3
3
|
import { env } from "cloudflare:workers";
|
|
4
4
|
export async function authenticateRequest(authConfig, providedRequest) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
5
|
+
const request = providedRequest || getRequest();
|
|
6
|
+
const authHeader = request.headers.get("authorization");
|
|
7
|
+
if (!authHeader) {
|
|
8
|
+
console.log("No auth header found");
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const token = extractBearerToken(authHeader);
|
|
12
|
+
if (!token) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const session = await verifySessionToken(token, authConfig);
|
|
17
|
+
return session;
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
console.error(JSON.stringify({
|
|
21
|
+
message: "Error verifying session token",
|
|
22
|
+
error: error instanceof Error ? error.message : String(error),
|
|
23
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
24
|
+
errorType: error instanceof Error ? error.constructor.name : "Unknown",
|
|
25
|
+
issuer: authConfig.issuer,
|
|
26
|
+
audience: authConfig.audience,
|
|
27
|
+
}));
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
31
30
|
}
|
|
32
31
|
async function verifySessionToken(token, config) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
};
|
|
57
|
-
const { payload } = await jwtVerify(token, localJWKS, options);
|
|
58
|
-
return payload;
|
|
32
|
+
const { issuer, audience } = config;
|
|
33
|
+
if (!issuer) {
|
|
34
|
+
throw new Error("Issuer must be provided for token verification");
|
|
35
|
+
}
|
|
36
|
+
if (!audience) {
|
|
37
|
+
throw new Error("Audience must be provided for token verification");
|
|
38
|
+
}
|
|
39
|
+
// Fetch JWKS - use service binding in production, direct fetch in development
|
|
40
|
+
const jwksResponse = import.meta.env.PROD && env.EVERY_APP_GATEWAY
|
|
41
|
+
? await env.EVERY_APP_GATEWAY.fetch("http://localhost/api/embedded/jwks")
|
|
42
|
+
: await fetch(`${env.GATEWAY_URL}/api/embedded/jwks`);
|
|
43
|
+
if (!jwksResponse.ok) {
|
|
44
|
+
throw new Error(`Failed to fetch JWKS: ${jwksResponse.status} ${jwksResponse.statusText}`);
|
|
45
|
+
}
|
|
46
|
+
const jwks = (await jwksResponse.json());
|
|
47
|
+
const localJWKS = createLocalJWKSet(jwks);
|
|
48
|
+
const options = {
|
|
49
|
+
issuer,
|
|
50
|
+
audience,
|
|
51
|
+
algorithms: ["RS256"],
|
|
52
|
+
};
|
|
53
|
+
const { payload } = await jwtVerify(token, localJWKS, options);
|
|
54
|
+
return payload;
|
|
59
55
|
}
|
|
60
56
|
/**
|
|
61
57
|
* Extracts the bearer token from an Authorization header.
|
|
@@ -64,8 +60,8 @@ async function verifySessionToken(token, config) {
|
|
|
64
60
|
* @returns The token string if valid, null otherwise
|
|
65
61
|
*/
|
|
66
62
|
export function extractBearerToken(authHeader) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return authHeader.substring(7);
|
|
71
67
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare function getLocalD1Url(): string | null;
|
|
2
|
-
//# sourceMappingURL=getLocalD1Url.d.ts.map
|
|
2
|
+
//# sourceMappingURL=getLocalD1Url.d.ts.map
|