@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
package/dist/profile.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export {
|
|
2
|
+
SUITE_COMMUNITY_APPLICATION_STATUSES,
|
|
3
|
+
SUITE_PROFILE_BIO_MAX_LENGTH,
|
|
4
|
+
SUITE_PROFILE_NAME_MAX_LENGTH,
|
|
5
|
+
SUITE_PROFILE_URL_MAX_LENGTH,
|
|
6
|
+
normalizeSuiteProfileLink,
|
|
7
|
+
parseSuiteCommunityProfileView,
|
|
8
|
+
parseSuiteProfileUpdateRequest,
|
|
9
|
+
parseSuiteProfileView,
|
|
10
|
+
normalizeSuiteProfileLinkV2,
|
|
11
|
+
parseSuiteAvatarRef,
|
|
12
|
+
parseSuiteProfileEditorV2,
|
|
13
|
+
parseSuiteProfileUpdateV2,
|
|
14
|
+
parseSuitePublicProfileV2,
|
|
15
|
+
suiteProfileAvatarEditorUrl,
|
|
16
|
+
suiteProfileAvatarPublicUrl,
|
|
17
|
+
} from "./identity/index.js";
|
|
@@ -0,0 +1,462 @@
|
|
|
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
|
+
export {
|
|
459
|
+
suiteAccountsPublicConfigFromEnvironment,
|
|
460
|
+
parseSuiteAccountsPublicConfig,
|
|
461
|
+
SUITE_ACCOUNTS_PUBLIC_ENVIRONMENT_KEYS
|
|
462
|
+
};
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// src/auth-client.ts
|
|
3
|
+
import { convexClient } from "@convex-dev/better-auth/client/plugins";
|
|
4
|
+
import { emailOTPClient } from "better-auth/client/plugins";
|
|
5
|
+
import { createAuthClient } from "better-auth/react";
|
|
6
|
+
function createSuiteAccountsAuthClient(options = {}) {
|
|
7
|
+
return createAuthClient({
|
|
8
|
+
basePath: options.basePath ?? "/api/auth",
|
|
9
|
+
plugins: [convexClient(), emailOTPClient()]
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
var suiteAccountsAuthClient = createSuiteAccountsAuthClient();
|
|
13
|
+
|
|
14
|
+
// src/react.tsx
|
|
15
|
+
import {
|
|
16
|
+
ConvexProviderWithAuth,
|
|
17
|
+
ConvexReactClient,
|
|
18
|
+
useConvexAuth
|
|
19
|
+
} from "convex/react";
|
|
20
|
+
import {
|
|
21
|
+
createContext,
|
|
22
|
+
useCallback,
|
|
23
|
+
useContext,
|
|
24
|
+
useEffect,
|
|
25
|
+
useMemo
|
|
26
|
+
} from "react";
|
|
27
|
+
import { jsx } from "react/jsx-runtime";
|
|
28
|
+
var SuiteAccountsContext = createContext(undefined);
|
|
29
|
+
function unavailableMessage(config) {
|
|
30
|
+
if (config.kind === "invalid")
|
|
31
|
+
return config.message;
|
|
32
|
+
if (config.kind === "unavailable")
|
|
33
|
+
return config.message;
|
|
34
|
+
if (config.kind === "missing") {
|
|
35
|
+
return `Missing suite Accounts configuration: ${config.missing.join(", ")}.`;
|
|
36
|
+
}
|
|
37
|
+
if (config.authMode === "oidc-rp") {
|
|
38
|
+
return "This product uses the suite OAuth relying-party session.";
|
|
39
|
+
}
|
|
40
|
+
return "Suite Accounts are unavailable.";
|
|
41
|
+
}
|
|
42
|
+
function SuiteAccountsContextBridge({
|
|
43
|
+
authClient,
|
|
44
|
+
children,
|
|
45
|
+
client,
|
|
46
|
+
config
|
|
47
|
+
}) {
|
|
48
|
+
const convexAuth = useConvexAuth();
|
|
49
|
+
const authentication = convexAuth.isLoading ? { kind: "loading" } : convexAuth.isAuthenticated ? { kind: "signed_in" } : { kind: "signed_out" };
|
|
50
|
+
const value = useMemo(() => ({ authClient, authentication, client, config }), [authClient, authentication, client, config]);
|
|
51
|
+
return /* @__PURE__ */ jsx(SuiteAccountsContext.Provider, {
|
|
52
|
+
value,
|
|
53
|
+
children
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function ReadySuiteAccountsProvider({
|
|
57
|
+
children,
|
|
58
|
+
config
|
|
59
|
+
}) {
|
|
60
|
+
const authClient = useMemo(() => createSuiteAccountsAuthClient({ basePath: config.authBasePath }), [config.authBasePath]);
|
|
61
|
+
const client = useMemo(() => new ConvexReactClient(config.convexUrl), [config.convexUrl]);
|
|
62
|
+
const { data: session, isPending } = authClient.useSession();
|
|
63
|
+
const sessionId = session?.session.id;
|
|
64
|
+
const fetchAccessToken = useCallback(async () => {
|
|
65
|
+
try {
|
|
66
|
+
const result = await authClient.convex.token({
|
|
67
|
+
fetchOptions: { throw: false }
|
|
68
|
+
});
|
|
69
|
+
return result.data?.token ?? null;
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}, [authClient]);
|
|
74
|
+
const useSuiteAuth = useCallback(() => ({
|
|
75
|
+
fetchAccessToken,
|
|
76
|
+
isAuthenticated: sessionId !== undefined,
|
|
77
|
+
isLoading: isPending
|
|
78
|
+
}), [fetchAccessToken, isPending, sessionId]);
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
return () => {
|
|
81
|
+
client.close();
|
|
82
|
+
};
|
|
83
|
+
}, [client]);
|
|
84
|
+
return /* @__PURE__ */ jsx(ConvexProviderWithAuth, {
|
|
85
|
+
client,
|
|
86
|
+
useAuth: useSuiteAuth,
|
|
87
|
+
children: /* @__PURE__ */ jsx(SuiteAccountsContextBridge, {
|
|
88
|
+
authClient,
|
|
89
|
+
client,
|
|
90
|
+
config,
|
|
91
|
+
children
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function SuiteAccountsProvider({
|
|
96
|
+
children,
|
|
97
|
+
config
|
|
98
|
+
}) {
|
|
99
|
+
if (config.kind === "ready" && (config.authMode === "authority" || config.authMode === "proxy")) {
|
|
100
|
+
return /* @__PURE__ */ jsx(ReadySuiteAccountsProvider, {
|
|
101
|
+
config,
|
|
102
|
+
children
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const value = {
|
|
106
|
+
authClient: null,
|
|
107
|
+
authentication: {
|
|
108
|
+
kind: "unavailable",
|
|
109
|
+
message: unavailableMessage(config)
|
|
110
|
+
},
|
|
111
|
+
client: null,
|
|
112
|
+
config
|
|
113
|
+
};
|
|
114
|
+
return /* @__PURE__ */ jsx(SuiteAccountsContext.Provider, {
|
|
115
|
+
value,
|
|
116
|
+
children
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function useSuiteAccounts() {
|
|
120
|
+
const value = useContext(SuiteAccountsContext);
|
|
121
|
+
if (value === undefined) {
|
|
122
|
+
throw new Error("useSuiteAccounts must be used inside SuiteAccountsProvider.");
|
|
123
|
+
}
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
function useSuiteAccountsClient() {
|
|
127
|
+
return useSuiteAccounts().client;
|
|
128
|
+
}
|
|
129
|
+
function useSuiteAccountsAuthentication() {
|
|
130
|
+
return useSuiteAccounts().authentication;
|
|
131
|
+
}
|
|
132
|
+
export {
|
|
133
|
+
useSuiteAccountsClient,
|
|
134
|
+
useSuiteAccountsAuthentication,
|
|
135
|
+
useSuiteAccounts,
|
|
136
|
+
SuiteAccountsProvider
|
|
137
|
+
};
|