@hraness/suite-accounts 0.9.20
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/LICENSE +21 -0
- package/README.md +496 -0
- package/compiler-foundation.css +1 -0
- package/dist/auth-client.js +16 -0
- package/dist/auth-proxy.js +757 -0
- package/dist/bearer-verifier.js +1119 -0
- package/dist/browser-session.js +208 -0
- package/dist/client-configuration.js +470 -0
- package/dist/convex-browser-auth-browser.js +923 -0
- package/dist/convex-browser-auth-server.js +846 -0
- package/dist/convex-browser-auth.js +597 -0
- package/dist/entitlements.js +502 -0
- package/dist/identity/functions.js +44 -0
- package/dist/identity/index.js +1716 -0
- package/dist/identity/return-targets.js +33 -0
- package/dist/index.js +1133 -0
- package/dist/oidc-device-code.js +253 -0
- package/dist/oidc-rp.js +2259 -0
- package/dist/oidc-session-policy.js +5 -0
- package/dist/oidc-surface-server.js +2332 -0
- package/dist/profile-form.js +1934 -0
- package/dist/profile.js +17 -0
- package/dist/public-config.js +462 -0
- package/dist/react.js +137 -0
- package/dist/receipt-verifier.js +619 -0
- package/dist/registry.js +327 -0
- package/dist/stylex-manifest.json +1 -0
- package/dist/stylex.css +435 -0
- package/dist/urls.js +391 -0
- package/package.json +263 -0
- package/portfolio-inventory.json +37 -0
- package/src/auth-client.ts +37 -0
- package/src/auth-proxy.ts +473 -0
- package/src/bearer-verifier.ts +478 -0
- package/src/browser-session.ts +260 -0
- package/src/client-configuration.ts +172 -0
- package/src/convex-browser-auth-browser.ts +238 -0
- package/src/convex-browser-auth-server.ts +492 -0
- package/src/convex-browser-auth.ts +208 -0
- package/src/convex-url.ts +74 -0
- package/src/entitlements.ts +317 -0
- package/src/identity/catalog.ts +227 -0
- package/src/identity/consumers.ts +53 -0
- package/src/identity/functions.ts +70 -0
- package/src/identity/identifiers.ts +82 -0
- package/src/identity/index.ts +10 -0
- package/src/identity/links.ts +280 -0
- package/src/identity/membership.ts +156 -0
- package/src/identity/principals.ts +301 -0
- package/src/identity/profiles-v2.ts +330 -0
- package/src/identity/profiles.ts +524 -0
- package/src/identity/return-targets.ts +20 -0
- package/src/identity/usernames.ts +101 -0
- package/src/identity/views.ts +289 -0
- package/src/immutable.ts +25 -0
- package/src/index.ts +7 -0
- package/src/oidc-continuation.ts +59 -0
- package/src/oidc-device-code.ts +401 -0
- package/src/oidc-rp.ts +2217 -0
- package/src/oidc-session-policy.ts +6 -0
- package/src/oidc-surface-server.ts +186 -0
- package/src/profile-form.css +1 -0
- package/src/profile-form.stylex.ts +226 -0
- package/src/profile-form.tsx +314 -0
- package/src/profile.ts +33 -0
- package/src/public-config.ts +294 -0
- package/src/public-profile-form-result.ts +82 -0
- package/src/public-profile-form.tsx +319 -0
- package/src/public-profile-links.ts +100 -0
- package/src/react.tsx +196 -0
- package/src/receipt-verifier.ts +273 -0
- package/src/registry.ts +600 -0
- package/src/urls.ts +234 -0
|
@@ -0,0 +1,757 @@
|
|
|
1
|
+
// src/identity/consumers.ts
|
|
2
|
+
import { err, ok } from "@hraness/result";
|
|
3
|
+
|
|
4
|
+
// src/immutable.ts
|
|
5
|
+
function deepFreeze(value) {
|
|
6
|
+
const visited = new WeakSet;
|
|
7
|
+
function freezeOwned(current) {
|
|
8
|
+
if (current === null || typeof current !== "object")
|
|
9
|
+
return;
|
|
10
|
+
if (visited.has(current))
|
|
11
|
+
return;
|
|
12
|
+
visited.add(current);
|
|
13
|
+
for (const key of Reflect.ownKeys(current)) {
|
|
14
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
15
|
+
if (descriptor !== undefined && "value" in descriptor) {
|
|
16
|
+
freezeOwned(descriptor.value);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
Object.freeze(current);
|
|
20
|
+
}
|
|
21
|
+
freezeOwned(value);
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/identity/consumers.ts
|
|
26
|
+
var SUITE_CONSUMER_IDS = deepFreeze([
|
|
27
|
+
"accounts",
|
|
28
|
+
"act60",
|
|
29
|
+
"elders",
|
|
30
|
+
"soundfish",
|
|
31
|
+
"oh-computer",
|
|
32
|
+
"draw-money",
|
|
33
|
+
"oprte",
|
|
34
|
+
"sponge"
|
|
35
|
+
]);
|
|
36
|
+
var LEGACY_SUITE_CONSUMER_IDS = deepFreeze([
|
|
37
|
+
"kitchen"
|
|
38
|
+
]);
|
|
39
|
+
function parseSuiteConsumerId(value) {
|
|
40
|
+
switch (value) {
|
|
41
|
+
case "accounts":
|
|
42
|
+
case "act60":
|
|
43
|
+
case "elders":
|
|
44
|
+
case "soundfish":
|
|
45
|
+
case "oh-computer":
|
|
46
|
+
case "draw-money":
|
|
47
|
+
case "oprte":
|
|
48
|
+
case "sponge":
|
|
49
|
+
return ok(value);
|
|
50
|
+
case "kitchen":
|
|
51
|
+
return ok("oprte");
|
|
52
|
+
default:
|
|
53
|
+
return err("invalid-consumer");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/registry.ts
|
|
58
|
+
var SUITE_ACCOUNTS_REMOTE_ENVIRONMENTS = deepFreeze([
|
|
59
|
+
"production"
|
|
60
|
+
]);
|
|
61
|
+
var accountsCookies = deepFreeze({
|
|
62
|
+
chunked: ["account_data", "session_data"],
|
|
63
|
+
names: [
|
|
64
|
+
"account_data",
|
|
65
|
+
"convex_jwt",
|
|
66
|
+
"dont_remember",
|
|
67
|
+
"session_data",
|
|
68
|
+
"session_token"
|
|
69
|
+
]
|
|
70
|
+
});
|
|
71
|
+
var consumerCookies = deepFreeze({
|
|
72
|
+
chunked: ["session_data"],
|
|
73
|
+
names: ["dont_remember", "session_data", "session_token"]
|
|
74
|
+
});
|
|
75
|
+
var SUITE_ACCOUNTS_DEPLOYMENTS = deepFreeze({
|
|
76
|
+
production: {
|
|
77
|
+
accountsOrigin: "https://account.hraness.com",
|
|
78
|
+
convexSiteUrl: "https://qualified-marmot-22.convex.site",
|
|
79
|
+
convexUrl: "https://qualified-marmot-22.convex.cloud"
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
function unsupported(siteUrl) {
|
|
83
|
+
return { billingReturn: { kind: "unsupported" }, siteUrl };
|
|
84
|
+
}
|
|
85
|
+
function oidcSite(id, displayName, productionSiteUrl) {
|
|
86
|
+
return {
|
|
87
|
+
auth: { basePath: "/api/suite-auth", kind: "oidc-rp" },
|
|
88
|
+
displayName,
|
|
89
|
+
environments: {
|
|
90
|
+
production: unsupported(productionSiteUrl)
|
|
91
|
+
},
|
|
92
|
+
id
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
var SUITE_ACCOUNTS_REGISTERED_CONSUMER_IDS = deepFreeze([
|
|
96
|
+
"accounts",
|
|
97
|
+
"act60",
|
|
98
|
+
"soundfish",
|
|
99
|
+
"oh-computer",
|
|
100
|
+
"draw-money",
|
|
101
|
+
"sponge"
|
|
102
|
+
]);
|
|
103
|
+
var SUITE_ACCOUNTS_CONSUMERS = deepFreeze({
|
|
104
|
+
accounts: {
|
|
105
|
+
auth: {
|
|
106
|
+
basePath: "/api/auth",
|
|
107
|
+
cookies: accountsCookies,
|
|
108
|
+
kind: "authority"
|
|
109
|
+
},
|
|
110
|
+
displayName: "Accounts",
|
|
111
|
+
environments: {
|
|
112
|
+
production: {
|
|
113
|
+
billingReturn: { kind: "supported", path: "/account" },
|
|
114
|
+
siteUrl: "https://account.hraness.com"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
id: "accounts"
|
|
118
|
+
},
|
|
119
|
+
act60: oidcSite("act60", "ACT60", "https://act60.me"),
|
|
120
|
+
soundfish: {
|
|
121
|
+
auth: { basePath: "/api/suite-auth", kind: "oidc-rp" },
|
|
122
|
+
displayName: "Soundfish",
|
|
123
|
+
environments: {
|
|
124
|
+
production: unsupported("https://sound.fish")
|
|
125
|
+
},
|
|
126
|
+
id: "soundfish"
|
|
127
|
+
},
|
|
128
|
+
"oh-computer": oidcSite("oh-computer", "Oh", "https://oh.computer"),
|
|
129
|
+
"draw-money": {
|
|
130
|
+
auth: {
|
|
131
|
+
basePath: "/api/auth",
|
|
132
|
+
cookies: consumerCookies,
|
|
133
|
+
kind: "proxy"
|
|
134
|
+
},
|
|
135
|
+
displayName: "Draw Money",
|
|
136
|
+
environments: {
|
|
137
|
+
production: unsupported("https://draw.money")
|
|
138
|
+
},
|
|
139
|
+
id: "draw-money"
|
|
140
|
+
},
|
|
141
|
+
sponge: oidcSite("sponge", "Sponge", "https://spongesearch.com")
|
|
142
|
+
});
|
|
143
|
+
var SUITE_ACCOUNTS_CURRENT_ORIGIN_OVERRIDES = deepFreeze({
|
|
144
|
+
sponge: {
|
|
145
|
+
production: unsupported("https://sponge.computer")
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
var SUITE_ACCOUNTS_CURRENT_CONSUMER_IDS = deepFreeze([
|
|
149
|
+
"accounts",
|
|
150
|
+
"act60",
|
|
151
|
+
"soundfish",
|
|
152
|
+
"oh-computer",
|
|
153
|
+
"sponge",
|
|
154
|
+
"peopleblade",
|
|
155
|
+
"aicharts",
|
|
156
|
+
"hraness",
|
|
157
|
+
"soulscrape",
|
|
158
|
+
"platonik",
|
|
159
|
+
"ghostget",
|
|
160
|
+
"alt",
|
|
161
|
+
"algal"
|
|
162
|
+
]);
|
|
163
|
+
function currentOidcSite(id, displayName, productionSiteUrl) {
|
|
164
|
+
return {
|
|
165
|
+
auth: { basePath: "/api/suite-auth", kind: "oidc-rp" },
|
|
166
|
+
displayName,
|
|
167
|
+
environments: {
|
|
168
|
+
production: unsupported(productionSiteUrl)
|
|
169
|
+
},
|
|
170
|
+
id
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
var SUITE_ACCOUNTS_CURRENT_CONSUMERS = deepFreeze({
|
|
174
|
+
accounts: SUITE_ACCOUNTS_CONSUMERS.accounts,
|
|
175
|
+
act60: SUITE_ACCOUNTS_CONSUMERS.act60,
|
|
176
|
+
soundfish: SUITE_ACCOUNTS_CONSUMERS.soundfish,
|
|
177
|
+
"oh-computer": SUITE_ACCOUNTS_CONSUMERS["oh-computer"],
|
|
178
|
+
sponge: currentOidcSite("sponge", "Sponge", SUITE_ACCOUNTS_CURRENT_ORIGIN_OVERRIDES.sponge.production.siteUrl),
|
|
179
|
+
peopleblade: currentOidcSite("peopleblade", "PeopleBlade", "https://peopleblade.com"),
|
|
180
|
+
aicharts: currentOidcSite("aicharts", "AI Charts", "https://aicharts.io"),
|
|
181
|
+
hraness: currentOidcSite("hraness", "Hraness", "https://hraness.com"),
|
|
182
|
+
soulscrape: currentOidcSite("soulscrape", "Soulscrape", "https://soulscrape.com"),
|
|
183
|
+
platonik: currentOidcSite("platonik", "Platonik", "https://platonik.space"),
|
|
184
|
+
ghostget: currentOidcSite("ghostget", "Ghostget", "https://ghostget.com"),
|
|
185
|
+
alt: currentOidcSite("alt", "Alt", "https://alt.dog"),
|
|
186
|
+
algal: currentOidcSite("algal", "Algal", "https://algal.cloud")
|
|
187
|
+
});
|
|
188
|
+
var SUITE_ACCOUNTS_ACTIVE_CONSUMER_IDS = deepFreeze([
|
|
189
|
+
"accounts",
|
|
190
|
+
"act60",
|
|
191
|
+
"soundfish",
|
|
192
|
+
"oh-computer",
|
|
193
|
+
"sponge"
|
|
194
|
+
]);
|
|
195
|
+
var SUITE_EMAIL_OTP_REQUIRED_OIDC_CONSUMER_IDS = deepFreeze(SUITE_ACCOUNTS_REGISTERED_CONSUMER_IDS.filter((consumer) => SUITE_ACCOUNTS_CONSUMERS[consumer].auth.kind === "oidc-rp"));
|
|
196
|
+
function suiteAccountsConsumerRequiresEmailOtp(consumer) {
|
|
197
|
+
return SUITE_EMAIL_OTP_REQUIRED_OIDC_CONSUMER_IDS.includes(consumer);
|
|
198
|
+
}
|
|
199
|
+
var SUITE_ACCOUNTS_LINKED_OIDC_CONSUMER_IDS = deepFreeze([
|
|
200
|
+
"soundfish"
|
|
201
|
+
]);
|
|
202
|
+
var SUITE_ACCOUNTS_CURRENT_EMAIL_OTP_REQUIRED_OIDC_CONSUMER_IDS = deepFreeze(SUITE_ACCOUNTS_CURRENT_CONSUMER_IDS.filter((consumer) => SUITE_ACCOUNTS_CURRENT_CONSUMERS[consumer].auth.kind === "oidc-rp"));
|
|
203
|
+
var SUITE_ACCOUNTS_CURRENT_LINKED_OIDC_CONSUMER_IDS = deepFreeze([
|
|
204
|
+
"soundfish",
|
|
205
|
+
"peopleblade",
|
|
206
|
+
"ghostget"
|
|
207
|
+
]);
|
|
208
|
+
var SUITE_ACCOUNTS_CURRENT_BROWSER_DEVICE_CODE_CONSUMER_IDS = deepFreeze([
|
|
209
|
+
"act60",
|
|
210
|
+
"soundfish",
|
|
211
|
+
"oh-computer",
|
|
212
|
+
"sponge",
|
|
213
|
+
"peopleblade",
|
|
214
|
+
"aicharts",
|
|
215
|
+
"hraness",
|
|
216
|
+
"soulscrape",
|
|
217
|
+
"platonik",
|
|
218
|
+
"ghostget",
|
|
219
|
+
"alt"
|
|
220
|
+
]);
|
|
221
|
+
var SUITE_ACCOUNTS_CURRENT_DEVICE_CLIENT_IDS = deepFreeze([
|
|
222
|
+
"algal-cli"
|
|
223
|
+
]);
|
|
224
|
+
var SUITE_ACCOUNTS_CURRENT_DEVICE_CLIENTS = deepFreeze({
|
|
225
|
+
"algal-cli": {
|
|
226
|
+
consumer: "algal",
|
|
227
|
+
displayName: "Algal CLI",
|
|
228
|
+
environments: ["production"],
|
|
229
|
+
id: "algal-cli"
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
function isSuiteAccountsConsumerId(value) {
|
|
233
|
+
return typeof value === "string" && SUITE_CONSUMER_IDS.includes(value);
|
|
234
|
+
}
|
|
235
|
+
function isSuiteAccountsRegisteredConsumerId(value) {
|
|
236
|
+
return typeof value === "string" && SUITE_ACCOUNTS_REGISTERED_CONSUMER_IDS.includes(value);
|
|
237
|
+
}
|
|
238
|
+
function isSuiteAccountsOidcConsumerId(value) {
|
|
239
|
+
return isSuiteAccountsRegisteredConsumerId(value) && SUITE_ACCOUNTS_CONSUMERS[value].auth.kind === "oidc-rp";
|
|
240
|
+
}
|
|
241
|
+
function isSuiteAccountsLinkedOidcConsumerId(value) {
|
|
242
|
+
return SUITE_ACCOUNTS_LINKED_OIDC_CONSUMER_IDS.includes(value);
|
|
243
|
+
}
|
|
244
|
+
function isSuiteAccountsOAuthConsumerId(value) {
|
|
245
|
+
return isSuiteAccountsRegisteredConsumerId(value) && SUITE_ACCOUNTS_CONSUMERS[value].auth.kind === "oidc-rp";
|
|
246
|
+
}
|
|
247
|
+
function isSuiteAccountsCurrentConsumerId(value) {
|
|
248
|
+
return typeof value === "string" && SUITE_ACCOUNTS_CURRENT_CONSUMER_IDS.includes(value);
|
|
249
|
+
}
|
|
250
|
+
function isSuiteAccountsCurrentOidcConsumerId(value) {
|
|
251
|
+
return getSuiteAccountsCurrentConsumer(value).auth.kind === "oidc-rp";
|
|
252
|
+
}
|
|
253
|
+
function isSuiteAccountsCurrentLinkedOidcConsumerId(value) {
|
|
254
|
+
return SUITE_ACCOUNTS_CURRENT_LINKED_OIDC_CONSUMER_IDS.includes(value);
|
|
255
|
+
}
|
|
256
|
+
function isSuiteAccountsCurrentOAuthConsumerId(value) {
|
|
257
|
+
return getSuiteAccountsCurrentConsumer(value).auth.kind === "oidc-rp";
|
|
258
|
+
}
|
|
259
|
+
function isSuiteAccountsCurrentBrowserDeviceCodeConsumerId(value) {
|
|
260
|
+
return typeof value === "string" && SUITE_ACCOUNTS_CURRENT_BROWSER_DEVICE_CODE_CONSUMER_IDS.includes(value);
|
|
261
|
+
}
|
|
262
|
+
function isSuiteAccountsCurrentDeviceClientId(value) {
|
|
263
|
+
return typeof value === "string" && SUITE_ACCOUNTS_CURRENT_DEVICE_CLIENT_IDS.includes(value);
|
|
264
|
+
}
|
|
265
|
+
function suiteAccountsCurrentConsumerRequiresEmailOtp(consumer) {
|
|
266
|
+
return SUITE_ACCOUNTS_CURRENT_EMAIL_OTP_REQUIRED_OIDC_CONSUMER_IDS.includes(consumer);
|
|
267
|
+
}
|
|
268
|
+
function getSuiteAccountsConsumer(consumer) {
|
|
269
|
+
return SUITE_ACCOUNTS_CONSUMERS[consumer];
|
|
270
|
+
}
|
|
271
|
+
function getSuiteAccountsConsumerEnvironment(consumer, environment) {
|
|
272
|
+
if (!isSuiteAccountsRegisteredConsumerId(consumer))
|
|
273
|
+
return null;
|
|
274
|
+
const registration = getSuiteAccountsConsumer(consumer);
|
|
275
|
+
return registration.environments[environment] ?? null;
|
|
276
|
+
}
|
|
277
|
+
function getSuiteAccountsCurrentConsumer(consumer) {
|
|
278
|
+
return SUITE_ACCOUNTS_CURRENT_CONSUMERS[consumer];
|
|
279
|
+
}
|
|
280
|
+
function getSuiteAccountsCurrentConsumerEnvironment(consumer, environment) {
|
|
281
|
+
if (!isSuiteAccountsCurrentConsumerId(consumer))
|
|
282
|
+
return null;
|
|
283
|
+
return getSuiteAccountsCurrentConsumer(consumer).environments[environment] ?? null;
|
|
284
|
+
}
|
|
285
|
+
function isSuiteAccountsActiveConsumerId(value) {
|
|
286
|
+
return SUITE_ACCOUNTS_ACTIVE_CONSUMER_IDS.includes(value);
|
|
287
|
+
}
|
|
288
|
+
function getSuiteAccountsDeployment(environment) {
|
|
289
|
+
return SUITE_ACCOUNTS_DEPLOYMENTS[environment];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/convex-url.ts
|
|
293
|
+
var LOCAL_HOSTNAMES = new Set(["127.0.0.1", "[::1]", "localhost"]);
|
|
294
|
+
function invalid(input, reason, message) {
|
|
295
|
+
return deepFreeze({ input, kind: "invalid", message, reason });
|
|
296
|
+
}
|
|
297
|
+
function parseConvexDeployment(value) {
|
|
298
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
299
|
+
return deepFreeze({ kind: "missing" });
|
|
300
|
+
}
|
|
301
|
+
const input = value.trim();
|
|
302
|
+
let parsed;
|
|
303
|
+
try {
|
|
304
|
+
parsed = new URL(input);
|
|
305
|
+
} catch {
|
|
306
|
+
return invalid(input, "not-a-url", "Use a complete Convex deployment URL.");
|
|
307
|
+
}
|
|
308
|
+
if (parsed.username !== "" || parsed.password !== "") {
|
|
309
|
+
return invalid(input, "credentials", "Deployment URLs cannot contain credentials.");
|
|
310
|
+
}
|
|
311
|
+
if (parsed.pathname !== "/" || parsed.search !== "" || parsed.hash !== "") {
|
|
312
|
+
return invalid(input, "not-an-origin", "Use the deployment origin without a path or query.");
|
|
313
|
+
}
|
|
314
|
+
const local = LOCAL_HOSTNAMES.has(parsed.hostname);
|
|
315
|
+
if (parsed.protocol !== "https:" && !(local && parsed.protocol === "http:")) {
|
|
316
|
+
return invalid(input, "insecure-remote", "Remote Convex deployments must use HTTPS.");
|
|
317
|
+
}
|
|
318
|
+
return deepFreeze({
|
|
319
|
+
kind: "ready",
|
|
320
|
+
origin: parsed.origin,
|
|
321
|
+
transport: local ? "local" : "cloud",
|
|
322
|
+
url: parsed.origin
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// src/public-config.ts
|
|
327
|
+
var LOOPBACK_HOSTS = new Set(["127.0.0.1", "[::1]", "localhost"]);
|
|
328
|
+
var SUITE_ACCOUNTS_PUBLIC_ENVIRONMENT_KEYS = deepFreeze([
|
|
329
|
+
"NEXT_PUBLIC_ACCOUNTS_CONVEX_URL",
|
|
330
|
+
"NEXT_PUBLIC_ACCOUNTS_CONVEX_SITE_URL",
|
|
331
|
+
"NEXT_PUBLIC_SITE_URL"
|
|
332
|
+
]);
|
|
333
|
+
function parseOrigin(value, field) {
|
|
334
|
+
let url;
|
|
335
|
+
try {
|
|
336
|
+
url = new URL(value);
|
|
337
|
+
} catch {
|
|
338
|
+
throw new Error(`${field} must be an absolute URL.`);
|
|
339
|
+
}
|
|
340
|
+
const loopback = LOOPBACK_HOSTS.has(url.hostname);
|
|
341
|
+
if (url.protocol !== "https:" && !(loopback && url.protocol === "http:")) {
|
|
342
|
+
throw new Error(`${field} must use HTTPS (HTTP is allowed only on loopback).`);
|
|
343
|
+
}
|
|
344
|
+
if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || url.pathname !== "" && url.pathname !== "/") {
|
|
345
|
+
throw new Error(`${field} must be a credential-free origin.`);
|
|
346
|
+
}
|
|
347
|
+
return new URL(url.origin);
|
|
348
|
+
}
|
|
349
|
+
function readyRemoteConfig(consumer, siteUrl, convexUrl, convexSiteUrl) {
|
|
350
|
+
const registration = getPublicConsumer(consumer);
|
|
351
|
+
const environment = "production";
|
|
352
|
+
const consumerEnvironment = getSuiteAccountsCurrentConsumerEnvironment(consumer, environment);
|
|
353
|
+
const deployment = getSuiteAccountsDeployment(environment);
|
|
354
|
+
if (consumerEnvironment?.siteUrl === siteUrl && deployment.convexUrl === convexUrl && deployment.convexSiteUrl === convexSiteUrl) {
|
|
355
|
+
return deepFreeze({
|
|
356
|
+
...publicAuthConfiguration(registration.auth),
|
|
357
|
+
canonicalProductOrigin: siteUrl,
|
|
358
|
+
consumer,
|
|
359
|
+
convexSiteUrl,
|
|
360
|
+
convexUrl,
|
|
361
|
+
environment,
|
|
362
|
+
kind: "ready",
|
|
363
|
+
siteUrl,
|
|
364
|
+
surfaceOrigin: siteUrl
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
function publicAuthConfiguration(auth) {
|
|
370
|
+
switch (auth.kind) {
|
|
371
|
+
case "authority":
|
|
372
|
+
return { authBasePath: auth.basePath, authMode: auth.kind };
|
|
373
|
+
case "oidc-rp":
|
|
374
|
+
return { authBasePath: auth.basePath, authMode: auth.kind };
|
|
375
|
+
case "proxy":
|
|
376
|
+
return { authBasePath: auth.basePath, authMode: auth.kind };
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function getPublicConsumer(consumer) {
|
|
380
|
+
return isSuiteAccountsCurrentConsumerId(consumer) ? getSuiteAccountsCurrentConsumer(consumer) : getSuiteAccountsConsumer(consumer);
|
|
381
|
+
}
|
|
382
|
+
function parseSuiteAccountsPublicConfig(consumer, environment) {
|
|
383
|
+
const missing = SUITE_ACCOUNTS_PUBLIC_ENVIRONMENT_KEYS.filter((name) => {
|
|
384
|
+
const value = environment[name];
|
|
385
|
+
return typeof value !== "string" || value.trim() === "";
|
|
386
|
+
});
|
|
387
|
+
if (missing.length > 0)
|
|
388
|
+
return deepFreeze({ kind: "missing", missing });
|
|
389
|
+
const site = parseOrigin(environment.NEXT_PUBLIC_SITE_URL, "NEXT_PUBLIC_SITE_URL");
|
|
390
|
+
const convexSite = parseOrigin(environment.NEXT_PUBLIC_ACCOUNTS_CONVEX_SITE_URL, "NEXT_PUBLIC_ACCOUNTS_CONVEX_SITE_URL");
|
|
391
|
+
const deployment = parseConvexDeployment(environment.NEXT_PUBLIC_ACCOUNTS_CONVEX_URL);
|
|
392
|
+
if (deployment.kind !== "ready") {
|
|
393
|
+
throw new Error(deployment.kind === "invalid" ? `NEXT_PUBLIC_ACCOUNTS_CONVEX_URL is invalid: ${deployment.message}` : "NEXT_PUBLIC_ACCOUNTS_CONVEX_URL is required.");
|
|
394
|
+
}
|
|
395
|
+
const convex = parseOrigin(deployment.url, "NEXT_PUBLIC_ACCOUNTS_CONVEX_URL");
|
|
396
|
+
const loopback = [site, convex, convexSite].map((url) => LOOPBACK_HOSTS.has(url.hostname));
|
|
397
|
+
if (loopback.every(Boolean)) {
|
|
398
|
+
if (consumer === "aicharts") {
|
|
399
|
+
throw new Error("AI Charts authentication requires its registered production origin.");
|
|
400
|
+
}
|
|
401
|
+
if (deployment.transport !== "local" || site.hostname !== convex.hostname || site.hostname !== convexSite.hostname) {
|
|
402
|
+
throw new Error("Local consumer and Accounts endpoints must use the same loopback host.");
|
|
403
|
+
}
|
|
404
|
+
const registration = getPublicConsumer(consumer);
|
|
405
|
+
return deepFreeze({
|
|
406
|
+
...publicAuthConfiguration(registration.auth),
|
|
407
|
+
canonicalProductOrigin: site.origin,
|
|
408
|
+
consumer,
|
|
409
|
+
convexSiteUrl: convexSite.origin,
|
|
410
|
+
convexUrl: convex.origin,
|
|
411
|
+
environment: "local",
|
|
412
|
+
kind: "ready",
|
|
413
|
+
siteUrl: site.origin,
|
|
414
|
+
surfaceOrigin: site.origin
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
if (loopback.some(Boolean)) {
|
|
418
|
+
throw new Error("Consumer and Accounts endpoints cannot mix local and remote environments.");
|
|
419
|
+
}
|
|
420
|
+
const previewSurfaceValue = environment.NEXT_PUBLIC_VERCEL_SURFACE_ORIGIN;
|
|
421
|
+
if (previewSurfaceValue !== undefined) {
|
|
422
|
+
const previewSurface = parseOrigin(previewSurfaceValue, "NEXT_PUBLIC_VERCEL_SURFACE_ORIGIN");
|
|
423
|
+
if (!previewSurface.hostname.endsWith(".vercel.app")) {
|
|
424
|
+
throw new Error("NEXT_PUBLIC_VERCEL_SURFACE_ORIGIN must use a generated .vercel.app origin.");
|
|
425
|
+
}
|
|
426
|
+
const production = readyRemoteConfig(consumer, site.origin, convex.origin, convexSite.origin);
|
|
427
|
+
if (production === null) {
|
|
428
|
+
throw new Error(`${getPublicConsumer(consumer).displayName} and Accounts endpoints ` + "do not match the production deployment.");
|
|
429
|
+
}
|
|
430
|
+
return deepFreeze({
|
|
431
|
+
canonicalProductOrigin: site.origin,
|
|
432
|
+
environment: "production",
|
|
433
|
+
kind: "unavailable",
|
|
434
|
+
message: "Suite authentication is unavailable on generated Vercel Preview origins.",
|
|
435
|
+
surfaceOrigin: previewSurface.origin
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
const remote = readyRemoteConfig(consumer, site.origin, convex.origin, convexSite.origin);
|
|
439
|
+
if (remote !== null)
|
|
440
|
+
return remote;
|
|
441
|
+
throw new Error(`${getPublicConsumer(consumer).displayName} and Accounts endpoints ` + "do not match an owned deployment environment.");
|
|
442
|
+
}
|
|
443
|
+
function suiteAccountsPublicConfigFromEnvironment(consumer, environment = {
|
|
444
|
+
NEXT_PUBLIC_VERCEL_SURFACE_ORIGIN: process.env.NEXT_PUBLIC_VERCEL_SURFACE_ORIGIN,
|
|
445
|
+
NEXT_PUBLIC_ACCOUNTS_CONVEX_SITE_URL: process.env.NEXT_PUBLIC_ACCOUNTS_CONVEX_SITE_URL,
|
|
446
|
+
NEXT_PUBLIC_ACCOUNTS_CONVEX_URL: process.env.NEXT_PUBLIC_ACCOUNTS_CONVEX_URL,
|
|
447
|
+
NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL
|
|
448
|
+
}) {
|
|
449
|
+
try {
|
|
450
|
+
return parseSuiteAccountsPublicConfig(consumer, environment);
|
|
451
|
+
} catch (error) {
|
|
452
|
+
return deepFreeze({
|
|
453
|
+
kind: "invalid",
|
|
454
|
+
message: error instanceof Error ? error.message : "Suite Accounts configuration is invalid."
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// src/auth-proxy.ts
|
|
460
|
+
var FORWARDED_REQUEST_HEADERS = [
|
|
461
|
+
"accept",
|
|
462
|
+
"accept-language",
|
|
463
|
+
"content-type",
|
|
464
|
+
"sec-fetch-dest",
|
|
465
|
+
"sec-fetch-mode",
|
|
466
|
+
"sec-fetch-site",
|
|
467
|
+
"sec-fetch-user"
|
|
468
|
+
];
|
|
469
|
+
var FORWARDED_RESPONSE_HEADERS = [
|
|
470
|
+
"content-language",
|
|
471
|
+
"content-type",
|
|
472
|
+
"vary"
|
|
473
|
+
];
|
|
474
|
+
var MAX_AUTH_REQUEST_BYTES = 1048576;
|
|
475
|
+
function errorResponse(code, message, status, retryable = false) {
|
|
476
|
+
return Response.json({
|
|
477
|
+
error: { code, message, retryable },
|
|
478
|
+
schemaVersion: 1
|
|
479
|
+
}, {
|
|
480
|
+
headers: {
|
|
481
|
+
"cache-control": "no-store",
|
|
482
|
+
pragma: "no-cache"
|
|
483
|
+
},
|
|
484
|
+
status
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
function unavailable() {
|
|
488
|
+
return Promise.resolve(errorResponse("SERVICE_NOT_CONFIGURED", "Suite Accounts authentication is not configured.", 503));
|
|
489
|
+
}
|
|
490
|
+
function cookiePrefix(config) {
|
|
491
|
+
return config.environment === "local" ? "cclrte-local" : "__Host-cclrte";
|
|
492
|
+
}
|
|
493
|
+
function cookieCapabilities(config) {
|
|
494
|
+
const auth = isSuiteAccountsCurrentConsumerId(config.consumer) ? getSuiteAccountsCurrentConsumer(config.consumer).auth : getSuiteAccountsConsumer(config.consumer).auth;
|
|
495
|
+
if (auth.kind !== "proxy") {
|
|
496
|
+
throw new Error("Only a registered suite auth proxy may forward cookies.");
|
|
497
|
+
}
|
|
498
|
+
return auth.cookies;
|
|
499
|
+
}
|
|
500
|
+
function cookieSuffix(name, config) {
|
|
501
|
+
const prefix = `${cookiePrefix(config)}.`;
|
|
502
|
+
return name.startsWith(prefix) ? name.slice(prefix.length) : null;
|
|
503
|
+
}
|
|
504
|
+
function enabledCookieName(name, config) {
|
|
505
|
+
const suffix = cookieSuffix(name, config);
|
|
506
|
+
if (suffix === null)
|
|
507
|
+
return false;
|
|
508
|
+
const capabilities = cookieCapabilities(config);
|
|
509
|
+
if (capabilities.names.includes(suffix))
|
|
510
|
+
return true;
|
|
511
|
+
const separator = suffix.lastIndexOf(".");
|
|
512
|
+
if (separator < 1)
|
|
513
|
+
return false;
|
|
514
|
+
const base = suffix.slice(0, separator);
|
|
515
|
+
const chunk = suffix.slice(separator + 1);
|
|
516
|
+
return capabilities.chunked.includes(base) && /^(?:0|[1-9]\d?)$/u.test(chunk);
|
|
517
|
+
}
|
|
518
|
+
function matchingOwnedCookiePrefix(name, config) {
|
|
519
|
+
return cookieSuffix(name, config) !== null;
|
|
520
|
+
}
|
|
521
|
+
function containsCookieControl(value) {
|
|
522
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
523
|
+
const code = value.charCodeAt(index);
|
|
524
|
+
if (code <= 31 || code === 127)
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
function filteredSuiteAuthCookieHeader(cookieHeader, config) {
|
|
530
|
+
if (cookieHeader === null)
|
|
531
|
+
return null;
|
|
532
|
+
const cookies = [];
|
|
533
|
+
for (const segment of cookieHeader.split(";")) {
|
|
534
|
+
const trimmed = segment.trim();
|
|
535
|
+
const separator = trimmed.indexOf("=");
|
|
536
|
+
if (separator <= 0)
|
|
537
|
+
continue;
|
|
538
|
+
const name = trimmed.slice(0, separator).trim();
|
|
539
|
+
const value = trimmed.slice(separator + 1).trim();
|
|
540
|
+
if (enabledCookieName(name, config) && !containsCookieControl(value) && !value.includes(";")) {
|
|
541
|
+
cookies.push(`${name}=${value}`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return cookies.length === 0 ? null : cookies.join("; ");
|
|
545
|
+
}
|
|
546
|
+
function requestIsAllowed(request, config) {
|
|
547
|
+
let incoming;
|
|
548
|
+
try {
|
|
549
|
+
incoming = new URL(request.url);
|
|
550
|
+
} catch {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
const onAuthPath = incoming.pathname === config.authBasePath || incoming.pathname.startsWith(`${config.authBasePath}/`);
|
|
554
|
+
if (incoming.origin !== config.siteUrl || !onAuthPath)
|
|
555
|
+
return false;
|
|
556
|
+
const origin = request.headers.get("origin");
|
|
557
|
+
if (origin !== null && origin !== config.siteUrl || request.method === "POST" && origin !== config.siteUrl) {
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
560
|
+
const fetchSite = request.headers.get("sec-fetch-site");
|
|
561
|
+
return fetchSite === null || fetchSite.toLowerCase() === "same-origin";
|
|
562
|
+
}
|
|
563
|
+
function forwardedRequestHeaders(request, config) {
|
|
564
|
+
const headers = new Headers;
|
|
565
|
+
headers.set("origin", config.siteUrl);
|
|
566
|
+
for (const name of FORWARDED_REQUEST_HEADERS) {
|
|
567
|
+
const value = request.headers.get(name);
|
|
568
|
+
if (value !== null)
|
|
569
|
+
headers.set(name, value);
|
|
570
|
+
}
|
|
571
|
+
const cookie = filteredSuiteAuthCookieHeader(request.headers.get("cookie"), config);
|
|
572
|
+
if (cookie !== null)
|
|
573
|
+
headers.set("cookie", cookie);
|
|
574
|
+
return headers;
|
|
575
|
+
}
|
|
576
|
+
function splitSetCookieHeader(value) {
|
|
577
|
+
return value.split(/,(?=\s*[!#$%&'*+\-.^_`|~0-9A-Za-z]+=)/u).map((cookie) => cookie.trim()).filter((cookie) => cookie !== "");
|
|
578
|
+
}
|
|
579
|
+
function responseCookies(headers) {
|
|
580
|
+
const cookieHeaders = headers;
|
|
581
|
+
const discrete = cookieHeaders.getSetCookie?.();
|
|
582
|
+
if (discrete !== undefined)
|
|
583
|
+
return discrete;
|
|
584
|
+
const combined = headers.get("set-cookie");
|
|
585
|
+
return combined === null ? [] : splitSetCookieHeader(combined);
|
|
586
|
+
}
|
|
587
|
+
function cookieName(cookie) {
|
|
588
|
+
if (/[\r\n]/u.test(cookie))
|
|
589
|
+
return null;
|
|
590
|
+
const pair = cookie.split(";", 1)[0]?.trim();
|
|
591
|
+
const separator = pair?.indexOf("=") ?? -1;
|
|
592
|
+
if (pair === undefined || separator <= 0)
|
|
593
|
+
return null;
|
|
594
|
+
const name = pair.slice(0, separator).trim();
|
|
595
|
+
return /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/u.test(name) ? name : null;
|
|
596
|
+
}
|
|
597
|
+
function validateOwnedCookie(cookie, config) {
|
|
598
|
+
const parts = cookie.split(";").map((value) => value.trim());
|
|
599
|
+
parts.shift();
|
|
600
|
+
const attributes = new Map;
|
|
601
|
+
for (const part of parts) {
|
|
602
|
+
if (part === "")
|
|
603
|
+
continue;
|
|
604
|
+
const separator = part.indexOf("=");
|
|
605
|
+
const name = (separator === -1 ? part : part.slice(0, separator)).trim().toLowerCase();
|
|
606
|
+
const value = separator === -1 ? null : part.slice(separator + 1).trim();
|
|
607
|
+
if (name === "" || attributes.has(name))
|
|
608
|
+
return false;
|
|
609
|
+
attributes.set(name, value);
|
|
610
|
+
}
|
|
611
|
+
const secure = config.environment === "local" ? !attributes.has("secure") : attributes.has("secure") && attributes.get("secure") === null;
|
|
612
|
+
return attributes.has("httponly") && attributes.get("httponly") === null && attributes.get("path") === "/" && attributes.get("samesite")?.toLowerCase() === "lax" && !attributes.has("domain") && secure;
|
|
613
|
+
}
|
|
614
|
+
function isSafeCookieDeletion(cookie, config) {
|
|
615
|
+
const pair = cookie.split(";", 1)[0]?.trim();
|
|
616
|
+
const separator = pair?.indexOf("=") ?? -1;
|
|
617
|
+
if (pair === undefined || separator <= 0 || pair.slice(separator + 1) !== "" || !validateOwnedCookie(cookie, config)) {
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
const maxAge = cookie.split(";").slice(1).find((attribute) => {
|
|
621
|
+
const [name] = attribute.trim().split("=", 1);
|
|
622
|
+
return name?.toLowerCase() === "max-age";
|
|
623
|
+
});
|
|
624
|
+
return maxAge?.trim().toLowerCase() === "max-age=0";
|
|
625
|
+
}
|
|
626
|
+
function validLocation(location, config) {
|
|
627
|
+
if (/[\r\n]/u.test(location) || location.startsWith("//"))
|
|
628
|
+
return false;
|
|
629
|
+
let url;
|
|
630
|
+
try {
|
|
631
|
+
url = new URL(location, config.siteUrl);
|
|
632
|
+
} catch {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
return url.origin === config.siteUrl && url.username === "" && url.password === "";
|
|
636
|
+
}
|
|
637
|
+
function safeUpstreamResponse(upstream, config) {
|
|
638
|
+
const headers = new Headers;
|
|
639
|
+
for (const name of FORWARDED_RESPONSE_HEADERS) {
|
|
640
|
+
const value = upstream.headers.get(name);
|
|
641
|
+
if (value !== null)
|
|
642
|
+
headers.set(name, value);
|
|
643
|
+
}
|
|
644
|
+
headers.set("cache-control", "no-store");
|
|
645
|
+
headers.set("pragma", "no-cache");
|
|
646
|
+
const location = upstream.headers.get("location");
|
|
647
|
+
if (location !== null) {
|
|
648
|
+
if (!validLocation(location, config)) {
|
|
649
|
+
return errorResponse("INVALID_AUTH_UPSTREAM_RESPONSE", "Suite Accounts returned an unsafe redirect.", 502, true);
|
|
650
|
+
}
|
|
651
|
+
headers.set("location", location);
|
|
652
|
+
}
|
|
653
|
+
for (const cookie of responseCookies(upstream.headers)) {
|
|
654
|
+
const name = cookieName(cookie);
|
|
655
|
+
if (name === null) {
|
|
656
|
+
return errorResponse("INVALID_AUTH_UPSTREAM_RESPONSE", "Suite Accounts returned a malformed cookie.", 502, true);
|
|
657
|
+
}
|
|
658
|
+
if (!matchingOwnedCookiePrefix(name, config))
|
|
659
|
+
continue;
|
|
660
|
+
if (!enabledCookieName(name, config)) {
|
|
661
|
+
if (isSafeCookieDeletion(cookie, config))
|
|
662
|
+
continue;
|
|
663
|
+
return errorResponse("INVALID_AUTH_UPSTREAM_RESPONSE", "Suite Accounts returned an unsafe session cookie.", 502, true);
|
|
664
|
+
}
|
|
665
|
+
if (!validateOwnedCookie(cookie, config)) {
|
|
666
|
+
return errorResponse("INVALID_AUTH_UPSTREAM_RESPONSE", "Suite Accounts returned an unsafe session cookie.", 502, true);
|
|
667
|
+
}
|
|
668
|
+
headers.append("set-cookie", cookie);
|
|
669
|
+
}
|
|
670
|
+
return new Response(upstream.body, {
|
|
671
|
+
headers,
|
|
672
|
+
status: upstream.status,
|
|
673
|
+
statusText: upstream.statusText
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
function upstreamPath(incoming, config) {
|
|
677
|
+
if (config.authBasePath === null) {
|
|
678
|
+
throw new Error("A CLI auth consumer has no proxy route.");
|
|
679
|
+
}
|
|
680
|
+
const suffix = incoming.pathname.slice(config.authBasePath.length);
|
|
681
|
+
return `/api/auth${suffix}${incoming.search}`;
|
|
682
|
+
}
|
|
683
|
+
async function readBoundedRequestBody(request, maximumBytes) {
|
|
684
|
+
if (request.body === null) {
|
|
685
|
+
return { bytes: new ArrayBuffer(0), kind: "ok" };
|
|
686
|
+
}
|
|
687
|
+
const reader = request.body.getReader();
|
|
688
|
+
const chunks = [];
|
|
689
|
+
let length = 0;
|
|
690
|
+
while (true) {
|
|
691
|
+
const result = await reader.read();
|
|
692
|
+
if (result.done)
|
|
693
|
+
break;
|
|
694
|
+
if (result.value.byteLength > maximumBytes - length) {
|
|
695
|
+
await reader.cancel();
|
|
696
|
+
return { kind: "too_large" };
|
|
697
|
+
}
|
|
698
|
+
length += result.value.byteLength;
|
|
699
|
+
chunks.push(result.value);
|
|
700
|
+
}
|
|
701
|
+
const bytes = new Uint8Array(length);
|
|
702
|
+
let offset = 0;
|
|
703
|
+
for (const chunk of chunks) {
|
|
704
|
+
bytes.set(chunk, offset);
|
|
705
|
+
offset += chunk.byteLength;
|
|
706
|
+
}
|
|
707
|
+
return { bytes: bytes.buffer, kind: "ok" };
|
|
708
|
+
}
|
|
709
|
+
async function proxyRequest(request, config, fetchImplementation) {
|
|
710
|
+
if (!requestIsAllowed(request, config)) {
|
|
711
|
+
return errorResponse("AUTH_PROXY_REQUEST_REJECTED", "The authentication request did not come from this environment.", 403);
|
|
712
|
+
}
|
|
713
|
+
const declaredLength = request.headers.get("content-length");
|
|
714
|
+
if (declaredLength !== null && (!/^(?:0|[1-9]\d*)$/u.test(declaredLength) || Number(declaredLength) > MAX_AUTH_REQUEST_BYTES)) {
|
|
715
|
+
return errorResponse("AUTH_REQUEST_TOO_LARGE", "The authentication request was too large.", 413);
|
|
716
|
+
}
|
|
717
|
+
const incoming = new URL(request.url);
|
|
718
|
+
const init = {
|
|
719
|
+
headers: forwardedRequestHeaders(request, config),
|
|
720
|
+
method: request.method,
|
|
721
|
+
redirect: "manual"
|
|
722
|
+
};
|
|
723
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
724
|
+
const body = await readBoundedRequestBody(request, MAX_AUTH_REQUEST_BYTES);
|
|
725
|
+
if (body.kind === "too_large") {
|
|
726
|
+
return errorResponse("AUTH_REQUEST_TOO_LARGE", "The authentication request was too large.", 413);
|
|
727
|
+
}
|
|
728
|
+
if (body.bytes.byteLength > 0)
|
|
729
|
+
init.body = body.bytes;
|
|
730
|
+
}
|
|
731
|
+
try {
|
|
732
|
+
const upstream = await fetchImplementation(new URL(upstreamPath(incoming, config), config.convexSiteUrl), init);
|
|
733
|
+
return safeUpstreamResponse(upstream, config);
|
|
734
|
+
} catch {
|
|
735
|
+
return errorResponse("AUTH_UPSTREAM_UNAVAILABLE", "Suite Accounts authentication is temporarily unavailable.", 502, true);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function suiteAccountsAuthServerForConfig(config, fetchImplementation = fetch) {
|
|
739
|
+
if (config.kind !== "ready" || config.authMode !== "proxy") {
|
|
740
|
+
return deepFreeze({ handler: { GET: unavailable, POST: unavailable } });
|
|
741
|
+
}
|
|
742
|
+
const ownedConfig = deepFreeze({ ...config });
|
|
743
|
+
return deepFreeze({
|
|
744
|
+
handler: {
|
|
745
|
+
GET: async (request) => await proxyRequest(request, ownedConfig, fetchImplementation),
|
|
746
|
+
POST: async (request) => await proxyRequest(request, ownedConfig, fetchImplementation)
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
function suiteAccountsAuthHandler(consumer, environment) {
|
|
751
|
+
return suiteAccountsAuthServerForConfig(suiteAccountsPublicConfigFromEnvironment(consumer, environment)).handler;
|
|
752
|
+
}
|
|
753
|
+
export {
|
|
754
|
+
suiteAccountsAuthServerForConfig,
|
|
755
|
+
suiteAccountsAuthHandler,
|
|
756
|
+
filteredSuiteAuthCookieHeader
|
|
757
|
+
};
|