@arcblock/did-connect-service 4.1.36 → 4.1.38

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.
Files changed (115) hide show
  1. package/dist/_generated/asset-bytes.js +1 -1
  2. package/dist/_generated/asset-manifest.js +6 -6
  3. package/dist/_generated/header-bundle-string.d.ts +1 -1
  4. package/dist/_generated/header-bundle-string.d.ts.map +1 -1
  5. package/dist/_generated/header-bundle-string.js +1 -1
  6. package/dist/_generated/header-bundle-string.js.map +1 -1
  7. package/dist/_generated/login-bundle-string.d.ts +1 -1
  8. package/dist/_generated/login-bundle-string.d.ts.map +1 -1
  9. package/dist/_generated/login-bundle-string.js +1 -1
  10. package/dist/_generated/login-bundle-string.js.map +1 -1
  11. package/dist/assets/{admin-core.69147606.js → admin-core.d42d34ce.js} +20 -0
  12. package/dist/assets/{admin-extra.979f5e56.js → admin-extra.c7a0720f.js} +3 -3
  13. package/dist/assets/{admin.76aa0931.css → admin.f5f577f9.css} +27 -0
  14. package/dist/assets/{header.94d9e46b.js → header.8703ab26.js} +5 -5
  15. package/dist/assets/login.60fd0777.js +731 -0
  16. package/dist/assets/{login.7b12c6dc.css → login.a9c8b434.css} +11 -0
  17. package/dist/handler-errors.d.ts +868 -3
  18. package/dist/handler-errors.d.ts.map +1 -1
  19. package/dist/handler-errors.js +1067 -26
  20. package/dist/handler-errors.js.map +1 -1
  21. package/dist/handlers/access-key-connect-handler.d.ts.map +1 -1
  22. package/dist/handlers/access-key-connect-handler.js +20 -11
  23. package/dist/handlers/access-key-connect-handler.js.map +1 -1
  24. package/dist/handlers/access-key-handler.d.ts.map +1 -1
  25. package/dist/handlers/access-key-handler.js +18 -7
  26. package/dist/handlers/access-key-handler.js.map +1 -1
  27. package/dist/handlers/account-merge-handler.d.ts +2 -0
  28. package/dist/handlers/account-merge-handler.d.ts.map +1 -1
  29. package/dist/handlers/account-merge-handler.js +34 -29
  30. package/dist/handlers/account-merge-handler.js.map +1 -1
  31. package/dist/handlers/admin-instance-handler.d.ts.map +1 -1
  32. package/dist/handlers/admin-instance-handler.js +21 -17
  33. package/dist/handlers/admin-instance-handler.js.map +1 -1
  34. package/dist/handlers/auth-handler.d.ts.map +1 -1
  35. package/dist/handlers/auth-handler.js +17 -10
  36. package/dist/handlers/auth-handler.js.map +1 -1
  37. package/dist/handlers/blocklet-js-handler.d.ts.map +1 -1
  38. package/dist/handlers/blocklet-js-handler.js +2 -1
  39. package/dist/handlers/blocklet-js-handler.js.map +1 -1
  40. package/dist/handlers/branding-handler.d.ts +1 -0
  41. package/dist/handlers/branding-handler.d.ts.map +1 -1
  42. package/dist/handlers/branding-handler.js +15 -8
  43. package/dist/handlers/branding-handler.js.map +1 -1
  44. package/dist/handlers/did-connect-handler.d.ts +1 -0
  45. package/dist/handlers/did-connect-handler.d.ts.map +1 -1
  46. package/dist/handlers/did-connect-handler.js +40 -18
  47. package/dist/handlers/did-connect-handler.js.map +1 -1
  48. package/dist/handlers/email-login-handler.d.ts +81 -0
  49. package/dist/handlers/email-login-handler.d.ts.map +1 -1
  50. package/dist/handlers/email-login-handler.js +124 -23
  51. package/dist/handlers/email-login-handler.js.map +1 -1
  52. package/dist/handlers/federation-admin-handler.js +2 -2
  53. package/dist/handlers/federation-admin-handler.js.map +1 -1
  54. package/dist/handlers/oauth-handler.d.ts +1 -0
  55. package/dist/handlers/oauth-handler.d.ts.map +1 -1
  56. package/dist/handlers/oauth-handler.js +42 -53
  57. package/dist/handlers/oauth-handler.js.map +1 -1
  58. package/dist/handlers/passkey-handler.d.ts +1 -0
  59. package/dist/handlers/passkey-handler.d.ts.map +1 -1
  60. package/dist/handlers/passkey-handler.js +51 -37
  61. package/dist/handlers/passkey-handler.js.map +1 -1
  62. package/dist/handlers/team-handler.d.ts +2 -0
  63. package/dist/handlers/team-handler.d.ts.map +1 -1
  64. package/dist/handlers/team-handler.js +58 -35
  65. package/dist/handlers/team-handler.js.map +1 -1
  66. package/dist/locale.d.ts +40 -4
  67. package/dist/locale.d.ts.map +1 -1
  68. package/dist/locale.js +109 -34
  69. package/dist/locale.js.map +1 -1
  70. package/dist/pages/admin/i18n.d.ts.map +1 -1
  71. package/dist/pages/admin/i18n.js +1415 -6
  72. package/dist/pages/admin/i18n.js.map +1 -1
  73. package/dist/pages/admin/index.d.ts.map +1 -1
  74. package/dist/pages/admin/index.js +5 -3
  75. package/dist/pages/admin/index.js.map +1 -1
  76. package/dist/pages/admin/styles.d.ts +1 -1
  77. package/dist/pages/admin/styles.d.ts.map +1 -1
  78. package/dist/pages/admin/styles.js +27 -0
  79. package/dist/pages/admin/styles.js.map +1 -1
  80. package/dist/pages/admin/tab-branding.d.ts.map +1 -1
  81. package/dist/pages/admin/tab-branding.js +4 -6
  82. package/dist/pages/admin/tab-branding.js.map +1 -1
  83. package/dist/pages/admin/tab-profile-accounts.d.ts.map +1 -1
  84. package/dist/pages/admin/tab-profile-accounts.js +20 -0
  85. package/dist/pages/admin/tab-profile-accounts.js.map +1 -1
  86. package/dist/pages/admin-instances-page.d.ts +218 -0
  87. package/dist/pages/admin-instances-page.d.ts.map +1 -1
  88. package/dist/pages/admin-instances-page.js +133 -24
  89. package/dist/pages/admin-instances-page.js.map +1 -1
  90. package/dist/pages/error-page.d.ts +37 -0
  91. package/dist/pages/error-page.d.ts.map +1 -1
  92. package/dist/pages/error-page.js +33 -1
  93. package/dist/pages/error-page.js.map +1 -1
  94. package/dist/pages/gen-access-key-page.d.ts +126 -0
  95. package/dist/pages/gen-access-key-page.d.ts.map +1 -1
  96. package/dist/pages/gen-access-key-page.js +72 -20
  97. package/dist/pages/gen-access-key-page.js.map +1 -1
  98. package/dist/pages/homepage.d.ts +98 -0
  99. package/dist/pages/homepage.d.ts.map +1 -1
  100. package/dist/pages/homepage.js +66 -15
  101. package/dist/pages/homepage.js.map +1 -1
  102. package/dist/pages/invite-page.d.ts +90 -0
  103. package/dist/pages/invite-page.d.ts.map +1 -1
  104. package/dist/pages/invite-page.js +66 -17
  105. package/dist/pages/invite-page.js.map +1 -1
  106. package/dist/pages/login-page.d.ts +8 -0
  107. package/dist/pages/login-page.d.ts.map +1 -1
  108. package/dist/pages/login-page.js +12 -2
  109. package/dist/pages/login-page.js.map +1 -1
  110. package/dist/pages/oauth-callback-page.d.ts +52 -1
  111. package/dist/pages/oauth-callback-page.d.ts.map +1 -1
  112. package/dist/pages/oauth-callback-page.js +85 -8
  113. package/dist/pages/oauth-callback-page.js.map +1 -1
  114. package/package.json +3 -3
  115. package/dist/assets/login.566f858e.js +0 -720
@@ -21,15 +21,17 @@
21
21
  * GET /.well-known/service/admin/team → admin SPA
22
22
  * GET /.well-known/service/invite → invite page
23
23
  */
24
+ import { AsyncLocalStorage } from "node:async_hooks";
24
25
  import { changeMembershipRole } from "../access/change-role.js";
25
26
  import { PermissionError, requirePermission } from "../access/rbac.js";
27
+ import { handlerError, localizeKnownHandlerError } from "../handler-errors.js";
26
28
  import { resolveInstanceRole } from "../identity/instance-role.js";
27
- import { detectLocale, detectTheme } from "../locale.js";
29
+ import { detectLocale, detectTheme, languageRowsForCodes, loadEnabledLocales, reconciledArcLangCookie } from "../locale.js";
28
30
  import { consoleLogger } from "../logger.js";
29
31
  import { buildLoginUrl } from "../login-url.js";
30
32
  import { LOGO_PREVIEW_BASE } from "../logo-paths.js";
31
33
  import { buildAdminPageHTML } from "../pages/admin/index.js";
32
- import { buildErrorPageHTML } from "../pages/error-page.js";
34
+ import { buildErrorPageHTML, errorPageCopy } from "../pages/error-page.js";
33
35
  import { buildInvitePageHTML } from "../pages/invite-page.js";
34
36
  import { parseBase64Image, sniffImageType } from "./avatar-handler.js";
35
37
  import { resolveAccessKeyCaller } from "./passkey-handler.js";
@@ -41,6 +43,8 @@ function escapeEmailHtml(value) {
41
43
  .replaceAll('"', """)
42
44
  .replaceAll("'", "'");
43
45
  }
46
+ /** Request-scoped so concurrent team fetches do not share a locale. */
47
+ const handlerLocale = new AsyncLocalStorage();
44
48
  const API_BASE = "/.well-known/service/api/team";
45
49
  const ADMIN_PAGE = "/.well-known/service/admin";
46
50
  const INVITE_PAGE = "/.well-known/service/invite";
@@ -124,6 +128,7 @@ function shapeConnectedAccounts(rows, userSourceProvider, userDid) {
124
128
  provider: a.provider,
125
129
  id: a.id,
126
130
  userInfo,
131
+ createdAt: a.createdAt ?? a.firstLoginAt ?? null,
127
132
  lastLoginAt: a.lastLoginAt ?? null,
128
133
  isMain: a.provider === userSourceProvider && a.did === userDid,
129
134
  ...(extra !== undefined ? { extra } : {}),
@@ -153,6 +158,9 @@ export class TeamHandler {
153
158
  this.rpName = options.rpName;
154
159
  this.logger = options.logger ?? consoleLogger;
155
160
  }
161
+ async localeFor(request, instanceDid) {
162
+ return detectLocale(request, await loadEnabledLocales(this.store, instanceDid ?? this.instanceDid));
163
+ }
156
164
  /** Resolve the portal base URL for building invitation links. */
157
165
  resolvePortalUrl(request) {
158
166
  if (this.portalUrl)
@@ -186,6 +194,11 @@ export class TeamHandler {
186
194
  }
187
195
  // ─── API Router ──────────────────────────────────────────────────────
188
196
  async handleAPI(request, path, url, instanceDid, context) {
197
+ const enabled = await loadEnabledLocales(this.store, instanceDid ?? this.instanceDid);
198
+ const locale = detectLocale(request, enabled);
199
+ return handlerLocale.run(locale, () => this.dispatchAPI(request, path, url, instanceDid, context));
200
+ }
201
+ async dispatchAPI(request, path, url, instanceDid, context) {
189
202
  const method = request.method;
190
203
  try {
191
204
  // Public endpoint: invitation info (no auth required)
@@ -215,7 +228,7 @@ export class TeamHandler {
215
228
  return await this.handleEmailUpdate(caller, request, instanceDid);
216
229
  }
217
230
  if (method === "POST" && path === "/profile/email/verify") {
218
- return await this.handleEmailVerifySend(caller, request);
231
+ return await this.handleEmailVerifySend(caller, request, instanceDid);
219
232
  }
220
233
  if (method === "PUT" && path === "/profile/email/verify") {
221
234
  return await this.handleEmailVerifyConfirm(caller, request, instanceDid);
@@ -537,10 +550,11 @@ export class TeamHandler {
537
550
  }
538
551
  /** PUT /profile/email — update email directly (marks as unverified). */
539
552
  async handleEmailUpdate(caller, request, instanceDid) {
553
+ const locale = await this.localeFor(request, instanceDid);
540
554
  const body = await this.parseJSON(request);
541
555
  const email = body.email?.toLowerCase().trim();
542
556
  if (!email?.includes("@")) {
543
- return this.jsonResponse({ error: "Invalid email address" }, 400);
557
+ return this.jsonResponse({ error: handlerError("invalidEmailAddress", locale) }, 400);
544
558
  }
545
559
  await this.store.updateUserProfile(caller.did, { email, emailVerified: 0 }, caller.did);
546
560
  // Record only the domain (after the final `@`) to avoid PII leakage.
@@ -557,19 +571,20 @@ export class TeamHandler {
557
571
  return this.jsonResponse({ ok: true, user });
558
572
  }
559
573
  /** POST /profile/email/verify — send verification code to current email. */
560
- async handleEmailVerifySend(caller, _request) {
574
+ async handleEmailVerifySend(caller, request, instanceDid) {
575
+ const locale = await this.localeFor(request, instanceDid);
561
576
  const emailConfig = await this.resolveEmailConfig();
562
577
  if (!emailConfig) {
563
- return this.jsonResponse({ error: "Email service is not configured" }, 503);
578
+ return this.jsonResponse({ error: handlerError("emailServiceNotConfigured", locale) }, 503);
564
579
  }
565
580
  const userInfo = await this.store.getMemberInfo(caller.did);
566
581
  const email = userInfo?.email;
567
582
  if (!email) {
568
- return this.jsonResponse({ error: "No email set on profile" }, 400);
583
+ return this.jsonResponse({ error: handlerError("noEmailOnProfile", locale) }, 400);
569
584
  }
570
585
  // Rate limit
571
586
  if (await this.store.isVerifyCodeSent(email)) {
572
- return this.jsonResponse({ error: "Code already sent, please wait" }, 429);
587
+ return this.jsonResponse({ error: handlerError("codeAlreadySent", locale) }, 429);
573
588
  }
574
589
  const code = Array.from({ length: 6 }, () => Math.floor(Math.random() * 10)).join("");
575
590
  await this.store.createVerifyCode(code, email, "email-verify");
@@ -596,32 +611,33 @@ export class TeamHandler {
596
611
  if (!res.ok) {
597
612
  const text = await res.text();
598
613
  this.logger.warn({ message: "resend api rejected email-change verification", mod: "team", status: res.status, body: text.slice(0, 200) });
599
- return this.jsonResponse({ error: `Failed to send email: ${text}` }, 500);
614
+ return this.jsonResponse({ error: handlerError("emailSendFailed", locale) }, 500);
600
615
  }
601
616
  }
602
617
  catch (err) {
603
618
  const message = err instanceof Error ? err.message : "Email send failed";
604
619
  this.logger.error({ message: "resend network error sending email-change verification", mod: "team", err: message });
605
- return this.jsonResponse({ error: message }, 500);
620
+ return this.jsonResponse({ error: handlerError("emailSendFailed", locale) }, 500);
606
621
  }
607
622
  await this.store.markVerifyCodeSent(code);
608
623
  return this.jsonResponse({ ok: true });
609
624
  }
610
625
  /** PUT /profile/email/verify — confirm verification code, mark email as verified. */
611
626
  async handleEmailVerifyConfirm(caller, request, instanceDid) {
627
+ const locale = await this.localeFor(request, instanceDid);
612
628
  const body = await this.parseJSON(request);
613
629
  const code = body.code?.trim();
614
630
  if (!code) {
615
- return this.jsonResponse({ error: "Verification code is required" }, 400);
631
+ return this.jsonResponse({ error: handlerError("verificationCodeRequired", locale) }, 400);
616
632
  }
617
633
  const userInfo = await this.store.getMemberInfo(caller.did);
618
634
  const email = userInfo?.email;
619
635
  if (!email) {
620
- return this.jsonResponse({ error: "No email set on profile" }, 400);
636
+ return this.jsonResponse({ error: handlerError("noEmailOnProfile", locale) }, 400);
621
637
  }
622
638
  const verified = await this.store.consumeVerifyCode(code);
623
639
  if (!verified || verified.subject !== email) {
624
- return this.jsonResponse({ error: "Invalid or expired verification code" }, 400);
640
+ return this.jsonResponse({ error: handlerError("invalidOrExpiredVerificationCode", locale) }, 400);
625
641
  }
626
642
  await this.store.updateUserProfile(caller.did, { emailVerified: 1 }, caller.did);
627
643
  await this.store.createAuditLog({
@@ -1495,7 +1511,8 @@ export class TeamHandler {
1495
1511
  async handleSaveOAuthProvider(caller, provider, request) {
1496
1512
  requirePermission(caller.role, "settings.edit_auth");
1497
1513
  if (!TeamHandler.VALID_OAUTH_PROVIDERS.includes(provider)) {
1498
- return this.errorResponse(`Invalid provider: ${provider}`, 400, "VALIDATION_ERROR");
1514
+ const locale = handlerLocale.getStore() ?? "en";
1515
+ return this.errorResponse(handlerError("invalidSettingsProvider", locale, { provider }), 400, "VALIDATION_ERROR");
1499
1516
  }
1500
1517
  const body = this.sanitizeKeys(await this.parseJSON(request));
1501
1518
  // Validate required fields based on provider
@@ -1567,7 +1584,8 @@ export class TeamHandler {
1567
1584
  for (const key of ["passkey", "email", "wallet"]) {
1568
1585
  const entry = body[key];
1569
1586
  if (entry && typeof entry.enabled !== "boolean") {
1570
- return this.errorResponse(`${key}.enabled must be a boolean`, 400, "VALIDATION_ERROR");
1587
+ const locale = handlerLocale.getStore() ?? "en";
1588
+ return this.errorResponse(handlerError("builtinEnabledMustBeBoolean", locale, { key }), 400, "VALIDATION_ERROR");
1571
1589
  }
1572
1590
  }
1573
1591
  const did = this.settingsInstanceDid;
@@ -1647,19 +1665,25 @@ export class TeamHandler {
1647
1665
  if (!user) {
1648
1666
  return new Response(null, { status: 302, headers: { Location: loginUrl } });
1649
1667
  }
1668
+ const brandingDid = instanceDid || this.instanceDid;
1669
+ const enabled = await loadEnabledLocales(this.store, brandingDid);
1650
1670
  if (!user.approved) {
1651
- return this.errorPage(403, "Account Blocked", "Your account has been blocked. Please contact the administrator.", url);
1671
+ const locale = detectLocale(request, enabled);
1672
+ const copy = errorPageCopy(locale);
1673
+ return this.errorPage(403, copy.accountBlockedTitle, copy.accountBlockedMessage, url, locale);
1652
1674
  }
1653
1675
  // Resolve effective role for instance context
1654
1676
  let effectiveRole = user.role ?? "guest";
1655
1677
  if (instanceDid) {
1656
1678
  const resolved = await resolveInstanceRole(this.store, caller.did, instanceDid, user.role ?? undefined);
1657
1679
  if (!resolved && !opts?.isUserPage) {
1658
- return this.errorPage(403, "Access Denied", "You are not a member of this team.", url);
1680
+ const locale = detectLocale(request, enabled);
1681
+ const copy = errorPageCopy(locale);
1682
+ return this.errorPage(403, copy.notTeamMemberTitle, copy.notTeamMemberMessage, url, locale);
1659
1683
  }
1660
1684
  effectiveRole = resolved ?? effectiveRole;
1661
1685
  }
1662
- const locale = detectLocale(request);
1686
+ const locale = detectLocale(request, enabled);
1663
1687
  const theme = detectTheme(request);
1664
1688
  // Read app branding + languages for admin header.
1665
1689
  //
@@ -1673,22 +1697,15 @@ export class TeamHandler {
1673
1697
  // pick between the two URLs is no longer needed.
1674
1698
  let appName = this.rpName || "App";
1675
1699
  const appLogo = `${LOGO_PREVIEW_BASE}/square`;
1676
- let languages;
1677
- const brandingDid = instanceDid || this.instanceDid;
1700
+ const languages = languageRowsForCodes(enabled);
1678
1701
  if (brandingDid) {
1679
1702
  try {
1680
- const [brandingRaw, langsRaw] = await Promise.all([
1681
- this.store.getSetting(brandingDid, "app:branding"),
1682
- this.store.getSetting(brandingDid, "app:languages"),
1683
- ]);
1703
+ const brandingRaw = await this.store.getSetting(brandingDid, "app:branding");
1684
1704
  if (brandingRaw) {
1685
1705
  const branding = JSON.parse(brandingRaw);
1686
1706
  if (branding.name)
1687
1707
  appName = branding.name;
1688
1708
  }
1689
- if (langsRaw) {
1690
- languages = JSON.parse(langsRaw);
1691
- }
1692
1709
  }
1693
1710
  catch { /* use defaults */ }
1694
1711
  }
@@ -1747,12 +1764,14 @@ export class TeamHandler {
1747
1764
  passkeyLoginEnabled,
1748
1765
  emailLoginEnabled,
1749
1766
  });
1750
- return new Response(html, {
1751
- headers: {
1752
- "Content-Type": "text/html; charset=utf-8",
1753
- "Cache-Control": "private, no-store",
1754
- },
1767
+ const headers = new Headers({
1768
+ "Content-Type": "text/html; charset=utf-8",
1769
+ "Cache-Control": "private, no-store",
1755
1770
  });
1771
+ const localeCookie = reconciledArcLangCookie(request, locale);
1772
+ if (localeCookie)
1773
+ headers.set("Set-Cookie", localeCookie);
1774
+ return new Response(html, { headers });
1756
1775
  }
1757
1776
  async serveInvitePage(request, ctxInstanceDid) {
1758
1777
  // Resolve federation config: if this instance is a federation member,
@@ -1768,7 +1787,8 @@ export class TeamHandler {
1768
1787
  masterOAuthOrigin = (await this.store.getSetting(groupScope, "masterUrl")) ?? undefined;
1769
1788
  }
1770
1789
  }
1771
- const locale = detectLocale(request);
1790
+ const enabled = await loadEnabledLocales(this.store, effectiveDid);
1791
+ const locale = detectLocale(request, enabled);
1772
1792
  const html = buildInvitePageHTML(this.apiBase, locale, {
1773
1793
  masterOAuthOrigin,
1774
1794
  appName: this.rpName || "App",
@@ -1781,11 +1801,12 @@ export class TeamHandler {
1781
1801
  });
1782
1802
  }
1783
1803
  // ─── Helpers ────────────────────────────────────────────────────────
1784
- errorPage(status, title, message, url) {
1804
+ errorPage(status, title, message, url, locale) {
1785
1805
  const html = buildErrorPageHTML({
1786
1806
  status,
1787
1807
  title,
1788
1808
  message,
1809
+ locale,
1789
1810
  action: { label: "Go to Homepage", href: "/" },
1790
1811
  secondaryAction: status === 403
1791
1812
  ? { label: "Sign in with another account", href: buildLoginUrl({ returnTo: url.pathname }) }
@@ -1806,7 +1827,9 @@ export class TeamHandler {
1806
1827
  });
1807
1828
  }
1808
1829
  errorResponse(message, status, code) {
1809
- return this.jsonResponse({ ok: false, error: message, code }, status);
1830
+ const locale = handlerLocale.getStore();
1831
+ const error = locale && status !== 401 ? localizeKnownHandlerError(message, locale) : message;
1832
+ return this.jsonResponse({ ok: false, error, code }, status);
1810
1833
  }
1811
1834
  async parseJSON(request) {
1812
1835
  try {