@seams/wallet-server 0.6.0 → 0.6.2

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.
@@ -8091,6 +8091,10 @@ function createRouterApiRouteDefinitions(options = {}) {
8091
8091
  plane: "public",
8092
8092
  proof: "webauthn",
8093
8093
  rationale: "Registration respond is authorized by the signed setup payload and the registration authority proof."
8094
+ }, ROUTER_API_WALLET_REGISTRATION_SESSION_SERVICES), publicRoute("wallet_registration_near_admission", "POST", "/wallets/register/near-admission", "Authorize deferred NEAR registration while ECDSA respond runs", {
8095
+ plane: "public",
8096
+ proof: "webauthn",
8097
+ rationale: "NEAR admission is authorized by the signed setup payload and the registration authority proof."
8094
8098
  }, ROUTER_API_WALLET_REGISTRATION_SESSION_SERVICES), publicRoute("wallet_registration_activate", "POST", "/wallets/register/activate", "Activate and finalize one wallet ECDSA registration", {
8095
8099
  plane: "public",
8096
8100
  proof: "threshold_protocol_state",
@@ -11110,6 +11114,9 @@ function isEmailOtpWalletRegistrationActivateSuccessV2(result) {
11110
11114
  function isPasskeyWalletRegistrationActivateSuccessV2(result) {
11111
11115
  return result.authMethod.kind === "passkey";
11112
11116
  }
11117
+ function isPasskeyWalletRegistrationNearProvisioningSuccessV2(result) {
11118
+ return result.authMethod.kind === "passkey";
11119
+ }
11113
11120
  function walletRegistrationRoutePolicyServices(input) {
11114
11121
  const service = input.services.walletRegistration;
11115
11122
  return {
@@ -12683,13 +12690,62 @@ async function handleRouterApiWalletRegistrationRespond(input) {
12683
12690
  if (!session) return routeError(500, "internal", "wallet registration respond requires session signing");
12684
12691
  const parsed = parseWalletRegistrationRespondRequest(input.body);
12685
12692
  if (!parsed.ok) return routeError(400, parsed.code, parsed.message);
12693
+ const serverTiming = [];
12686
12694
  const result = await input.services.walletRegistration.respondWalletRegistration({
12687
12695
  ...parsed.value,
12688
12696
  verifier: session,
12689
12697
  ...registrationUserAgentFromHeaders(input.headers) ? { userAgent: registrationUserAgentFromHeaders(input.headers) } : {}
12690
- }, traceContext.value ?? void 0);
12698
+ }, traceContext.value ?? void 0, serverTiming);
12699
+ return routeJson(result.ok ? 200 : 400, result, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
12700
+ }
12701
+ async function handleRouterApiWalletRegistrationNearAdmission(input) {
12702
+ const traceContext = parseRegistrationTraceContext(input.headers);
12703
+ if (!traceContext.ok) return routeError(400, "invalid_trace_id", traceContext.message);
12704
+ if (!isPlainObject$1(input.body)) return routeError(400, "invalid_body", "JSON body required");
12705
+ const session = input.services.session;
12706
+ if (!session) return routeError(500, "internal", "NEAR admission requires setup verification");
12707
+ const body = input.body;
12708
+ const registrationCeremonyId = String(body.registrationCeremonyId || "").trim();
12709
+ const signedSetup = String(body.signedSetup || "").trim();
12710
+ if (!registrationCeremonyId || !signedSetup) return routeError(400, "invalid_body", "registrationCeremonyId and signedSetup are required");
12711
+ const authority = parseWalletRegistrationAuthorityProof(body);
12712
+ if (!authority.ok) return routeError(400, authority.code, authority.message);
12713
+ const result = await input.services.walletRegistration.authorizeWalletRegistrationNearAdmission({
12714
+ registrationCeremonyId,
12715
+ signedSetup,
12716
+ authority: authority.value,
12717
+ verifier: session,
12718
+ ...registrationUserAgentFromHeaders(input.headers) ? { userAgent: registrationUserAgentFromHeaders(input.headers) } : {}
12719
+ });
12691
12720
  return routeJson(result.ok ? 200 : 400, result);
12692
12721
  }
12722
+ function parseWalletRegistrationAuthorityProof(body) {
12723
+ const hasWebAuthn = Object.prototype.hasOwnProperty.call(body, "webauthn_registration");
12724
+ if (hasWebAuthn === Object.prototype.hasOwnProperty.call(body, "emailOtpRegistrationProof")) return {
12725
+ ok: false,
12726
+ code: "invalid_body",
12727
+ message: "exactly one registration authority proof is required"
12728
+ };
12729
+ if (hasWebAuthn) return {
12730
+ ok: true,
12731
+ value: {
12732
+ kind: "passkey",
12733
+ webauthnRegistration: body.webauthn_registration
12734
+ }
12735
+ };
12736
+ const proof = normalizeEmailOtpRegistrationProof(body.emailOtpRegistrationProof);
12737
+ return proof ? {
12738
+ ok: true,
12739
+ value: {
12740
+ kind: "email_otp",
12741
+ emailOtpRegistrationProof: proof
12742
+ }
12743
+ } : {
12744
+ ok: false,
12745
+ code: "invalid_body",
12746
+ message: "emailOtpRegistrationProof is invalid"
12747
+ };
12748
+ }
12693
12749
  /**
12694
12750
  * Parses respond's public body. `signedSetup` stays an opaque string here —
12695
12751
  * the service verifies it; parsing its contents at the boundary would invite
@@ -12724,29 +12780,9 @@ function parseWalletRegistrationRespondRequest(body) {
12724
12780
  code: "invalid_body",
12725
12781
  message: "an Ed25519-only respond carries no ECDSA registration"
12726
12782
  };
12727
- const hasWebAuthn = Object.prototype.hasOwnProperty.call(body, "webauthn_registration");
12728
- if (hasWebAuthn === Object.prototype.hasOwnProperty.call(body, "emailOtpRegistrationProof")) return {
12729
- ok: false,
12730
- code: "invalid_body",
12731
- message: "exactly one registration authority proof is required"
12732
- };
12733
- let authority;
12734
- if (hasWebAuthn) authority = {
12735
- kind: "passkey",
12736
- webauthnRegistration: body.webauthn_registration
12737
- };
12738
- else {
12739
- const proof = normalizeEmailOtpRegistrationProof(body.emailOtpRegistrationProof);
12740
- if (!proof) return {
12741
- ok: false,
12742
- code: "invalid_body",
12743
- message: "emailOtpRegistrationProof is invalid"
12744
- };
12745
- authority = {
12746
- kind: "email_otp",
12747
- emailOtpRegistrationProof: proof
12748
- };
12749
- }
12783
+ const parsedAuthority = parseWalletRegistrationAuthorityProof(body);
12784
+ if (!parsedAuthority.ok) return parsedAuthority;
12785
+ const authority = parsedAuthority.value;
12750
12786
  if (planKind === "near_ed25519") return {
12751
12787
  ok: true,
12752
12788
  value: {
@@ -12873,6 +12909,7 @@ async function handleRouterApiWalletRegistrationActivate(input) {
12873
12909
  } catch {
12874
12910
  return routeError(400, "invalid_body", "ECDSA activation facts are invalid");
12875
12911
  }
12912
+ const serverTiming = [];
12876
12913
  const result = await input.services.walletRegistration.activateWalletRegistration({
12877
12914
  registrationCeremonyId,
12878
12915
  signedSetup,
@@ -12882,7 +12919,7 @@ async function handleRouterApiWalletRegistrationActivate(input) {
12882
12919
  ...body.emailOtpEnrollment ? { emailOtpEnrollment: body.emailOtpEnrollment } : {},
12883
12920
  ...body.walletCustodyCommit !== void 0 ? { walletCustodyCommit: body.walletCustodyCommit } : {},
12884
12921
  verifier: session
12885
- }, traceContext.value ?? void 0);
12922
+ }, traceContext.value ?? void 0, serverTiming);
12886
12923
  if (!result.ok) return routeJson(400, result);
12887
12924
  if (!isEmailOtpWalletRegistrationActivateSuccessV2(result) && !isPasskeyWalletRegistrationActivateSuccessV2(result)) return routeError(500, "internal", "Wallet registration activation returned an invalid authority");
12888
12925
  const projectionError = await projectActivatedWallet({
@@ -12891,7 +12928,7 @@ async function handleRouterApiWalletRegistrationActivate(input) {
12891
12928
  signedSetup
12892
12929
  });
12893
12930
  if (projectionError) return routeError(500, "internal", `Wallet was created but its Console projection failed: ${projectionError}`);
12894
- return routeJson(200, result);
12931
+ return routeJson(200, result, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
12895
12932
  }
12896
12933
  /**
12897
12934
  * Refactor 94C. `POST /wallets/register/near-provisioning` — the non-blocking
@@ -12909,6 +12946,9 @@ async function handleRouterApiWalletRegistrationNearProvisioning(input) {
12909
12946
  if (!registrationCeremonyId || !signedSetup || !idempotencyKey) return routeError(400, "invalid_body", "registrationCeremonyId, signedSetup and idempotencyKey are required");
12910
12947
  const ed25519 = parseWalletRegistrationEd25519Finalize(body.ed25519);
12911
12948
  if (!ed25519.ok) return routeError(400, ed25519.code, ed25519.message);
12949
+ const sessionSeal = parseRegistrationPreparedSessionSeal(body.sessionSeal);
12950
+ if (!sessionSeal.ok) return routeError(400, sessionSeal.code, sessionSeal.message);
12951
+ const serverTiming = [];
12912
12952
  const result = await input.services.walletRegistration.completeWalletRegistrationNearProvisioning({
12913
12953
  authorization: registrationContinuationAuthorization(input.headers),
12914
12954
  registrationCeremonyId,
@@ -12918,9 +12958,110 @@ async function handleRouterApiWalletRegistrationNearProvisioning(input) {
12918
12958
  emailOtpEnrollment: body.emailOtpEnrollment,
12919
12959
  ...body.walletCustodyCommit !== void 0 ? { walletCustodyCommit: body.walletCustodyCommit } : {},
12920
12960
  verifier: session
12921
- });
12961
+ }, serverTiming);
12922
12962
  if (!result.ok) return routeJson(400, result);
12923
- return routeJson(200, result);
12963
+ if (sessionSeal.value) {
12964
+ if (!isPasskeyWalletRegistrationNearProvisioningSuccessV2(result)) return routeError(400, "invalid_body", "Registration session sealing requires a passkey authority");
12965
+ const sealStartedAt = Date.now();
12966
+ const sealed = await applyRegistrationPreparedSessionSeal({
12967
+ options: input.signingSessionSeal,
12968
+ request: sessionSeal.value,
12969
+ response: result
12970
+ });
12971
+ serverTiming.push(["near_finalize_session_seal", Math.max(0, Date.now() - sealStartedAt)]);
12972
+ if (!sealed.ok) return routeJson(503, {
12973
+ ok: false,
12974
+ code: sealed.code,
12975
+ message: sealed.message
12976
+ }, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
12977
+ return routeJson(200, {
12978
+ ...result,
12979
+ sessionSeal: sealed.value
12980
+ }, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
12981
+ }
12982
+ return routeJson(200, result, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
12983
+ }
12984
+ function parseRegistrationPreparedSessionSeal(raw) {
12985
+ if (raw === void 0) return {
12986
+ ok: true,
12987
+ value: void 0
12988
+ };
12989
+ if (!isPlainObject$1(raw)) return {
12990
+ ok: false,
12991
+ code: "invalid_body",
12992
+ message: "sessionSeal must be an object"
12993
+ };
12994
+ if (Object.keys(raw).some((field) => ![
12995
+ "thresholdSessionId",
12996
+ "ciphertext",
12997
+ "keyVersion"
12998
+ ].includes(field))) return {
12999
+ ok: false,
13000
+ code: "invalid_body",
13001
+ message: "sessionSeal has unknown fields"
13002
+ };
13003
+ const thresholdSessionId = String(raw.thresholdSessionId || "").trim();
13004
+ const ciphertext = String(raw.ciphertext || "").trim();
13005
+ const keyVersion = String(raw.keyVersion || "").trim();
13006
+ if (!thresholdSessionId || !ciphertext) return {
13007
+ ok: false,
13008
+ code: "invalid_body",
13009
+ message: "sessionSeal thresholdSessionId and ciphertext are required"
13010
+ };
13011
+ return {
13012
+ ok: true,
13013
+ value: {
13014
+ thresholdSessionId,
13015
+ ciphertext,
13016
+ ...keyVersion ? { keyVersion } : {}
13017
+ }
13018
+ };
13019
+ }
13020
+ async function applyRegistrationPreparedSessionSeal(args) {
13021
+ if (!args.options) return {
13022
+ ok: false,
13023
+ code: "signing_session_seal_unavailable",
13024
+ message: "Signing-session sealing is unavailable"
13025
+ };
13026
+ const session = args.response.registrationEstablishedSession.session;
13027
+ const tokens = session.tokens;
13028
+ const ed25519 = tokens.kind === "near_ed25519" || tokens.kind === "near_ed25519_and_evm_family_ecdsa" ? tokens.ed25519 : null;
13029
+ if (!ed25519 || String(ed25519.thresholdSessionId) !== args.request.thresholdSessionId) return {
13030
+ ok: false,
13031
+ code: "invalid_session_seal",
13032
+ message: "Prepared seal does not match the established Ed25519 session"
13033
+ };
13034
+ const sealed = await args.options.service.applyServerSeal(args.request, {
13035
+ userId: String(session.walletId),
13036
+ session: {
13037
+ kind: "exact_wallet_session_operation_credential",
13038
+ curve: "ed25519",
13039
+ thresholdSessionId: args.request.thresholdSessionId,
13040
+ userId: String(session.walletId),
13041
+ expiresAtMs: session.expiresAtMs
13042
+ }
13043
+ });
13044
+ if (!sealed.ok) return sealed;
13045
+ const keyVersion = String(sealed.keyVersion || "").trim();
13046
+ if (!keyVersion) return {
13047
+ ok: false,
13048
+ code: "invalid_session_seal",
13049
+ message: "Signing-session seal response is missing its key version"
13050
+ };
13051
+ return {
13052
+ ok: true,
13053
+ value: {
13054
+ ciphertext: sealed.ciphertext,
13055
+ keyVersion,
13056
+ expiresAtMs: Math.min(sealed.expiresAtMs ?? session.expiresAtMs, session.expiresAtMs),
13057
+ remainingUses: session.remainingUses
13058
+ }
13059
+ };
13060
+ }
13061
+ function ecdsaGatewayServerTimingHeaders(serverTiming) {
13062
+ if (serverTiming.length === 0) return void 0;
13063
+ const header = serverTiming.map(([name, durationMs]) => `${name};dur=${Math.max(0, durationMs).toFixed(1)}`).join(", ");
13064
+ return { "Server-Timing": header };
12924
13065
  }
12925
13066
  async function handleRouterApiWalletAddSignerStart(input) {
12926
13067
  if (!isPlainObject$1(input.body)) return routeError(400, "invalid_body", "JSON body required");
@@ -13279,6 +13420,7 @@ function routerApiEmailOtpRouteService(service) {
13279
13420
  const ROUTE_IDS = [
13280
13421
  "wallet_registration_setup",
13281
13422
  "wallet_registration_respond",
13423
+ "wallet_registration_near_admission",
13282
13424
  "wallet_registration_activate",
13283
13425
  "wallet_registration_near_provisioning",
13284
13426
  "wallet_add_signer_intent",
@@ -13334,9 +13476,64 @@ async function handleWalletRegistration(ctx) {
13334
13476
  publishableKeyAuth: ctx.opts.publishableKeyAuth,
13335
13477
  walletProjection: ctx.opts.walletProjection
13336
13478
  },
13337
- sourceIp: resolveSourceIpFromFetchHeaders(ctx.request.headers) || void 0
13479
+ sourceIp: resolveSourceIpFromFetchHeaders(ctx.request.headers) || void 0,
13480
+ signingSessionSeal: ctx.opts.signingSessionSeal
13338
13481
  };
13339
- return toFetchRouteResponse(route.id === "wallet_registration_setup" ? await handleRouterApiWalletRegistrationSetup(common) : route.id === "wallet_registration_respond" ? await handleRouterApiWalletRegistrationRespond(common) : route.id === "wallet_registration_activate" ? await handleRouterApiWalletRegistrationActivate(common) : route.id === "wallet_registration_near_provisioning" ? await handleRouterApiWalletRegistrationNearProvisioning(common) : route.id === "wallet_add_signer_intent" ? await handleRouterApiWalletAddSignerIntent(common) : route.id === "wallet_add_signer_start" ? await handleRouterApiWalletAddSignerStart(common) : route.id === "wallet_add_signer_ecdsa_derivation_respond" ? await handleRouterApiWalletAddSignerEcdsaDerivationRespond(common) : route.id === "wallet_add_signer_ecdsa_activation" ? await handleRouterApiWalletAddSignerEcdsaActivation(common) : route.id === "wallet_add_signer_finalize" ? await handleRouterApiWalletAddSignerFinalize(common) : route.id === "wallet_add_auth_method_intent" ? await handleRouterApiWalletAddAuthMethodIntent(common) : route.id === "wallet_add_auth_method_email_otp_challenge" ? await handleRouterApiWalletAddAuthMethodEmailOtpChallenge(common) : route.id === "wallet_add_auth_method_start" ? await handleRouterApiWalletAddAuthMethodStart(common) : route.id === "wallet_add_auth_method_finalize" ? await handleRouterApiWalletAddAuthMethodFinalize(common) : route.id === "wallet_revoke_auth_method" ? await handleRouterApiWalletRevokeAuthMethod(common) : route.id === "wallet_ecdsa_key_facts_inventory" ? await handleRouterApiWalletEcdsaKeyFactsInventory(common) : await handleRouterApiWalletNearImplicitAccountFund(common));
13482
+ let response;
13483
+ switch (route.id) {
13484
+ case "wallet_registration_setup":
13485
+ response = await handleRouterApiWalletRegistrationSetup(common);
13486
+ break;
13487
+ case "wallet_registration_respond":
13488
+ response = await handleRouterApiWalletRegistrationRespond(common);
13489
+ break;
13490
+ case "wallet_registration_near_admission":
13491
+ response = await handleRouterApiWalletRegistrationNearAdmission(common);
13492
+ break;
13493
+ case "wallet_registration_activate":
13494
+ response = await handleRouterApiWalletRegistrationActivate(common);
13495
+ break;
13496
+ case "wallet_registration_near_provisioning":
13497
+ response = await handleRouterApiWalletRegistrationNearProvisioning(common);
13498
+ break;
13499
+ case "wallet_add_signer_intent":
13500
+ response = await handleRouterApiWalletAddSignerIntent(common);
13501
+ break;
13502
+ case "wallet_add_signer_start":
13503
+ response = await handleRouterApiWalletAddSignerStart(common);
13504
+ break;
13505
+ case "wallet_add_signer_ecdsa_derivation_respond":
13506
+ response = await handleRouterApiWalletAddSignerEcdsaDerivationRespond(common);
13507
+ break;
13508
+ case "wallet_add_signer_ecdsa_activation":
13509
+ response = await handleRouterApiWalletAddSignerEcdsaActivation(common);
13510
+ break;
13511
+ case "wallet_add_signer_finalize":
13512
+ response = await handleRouterApiWalletAddSignerFinalize(common);
13513
+ break;
13514
+ case "wallet_add_auth_method_intent":
13515
+ response = await handleRouterApiWalletAddAuthMethodIntent(common);
13516
+ break;
13517
+ case "wallet_add_auth_method_email_otp_challenge":
13518
+ response = await handleRouterApiWalletAddAuthMethodEmailOtpChallenge(common);
13519
+ break;
13520
+ case "wallet_add_auth_method_start":
13521
+ response = await handleRouterApiWalletAddAuthMethodStart(common);
13522
+ break;
13523
+ case "wallet_add_auth_method_finalize":
13524
+ response = await handleRouterApiWalletAddAuthMethodFinalize(common);
13525
+ break;
13526
+ case "wallet_revoke_auth_method":
13527
+ response = await handleRouterApiWalletRevokeAuthMethod(common);
13528
+ break;
13529
+ case "wallet_ecdsa_key_facts_inventory":
13530
+ response = await handleRouterApiWalletEcdsaKeyFactsInventory(common);
13531
+ break;
13532
+ default:
13533
+ response = await handleRouterApiWalletNearImplicitAccountFund(common);
13534
+ break;
13535
+ }
13536
+ return toFetchRouteResponse(response);
13340
13537
  }
13341
13538
  //#endregion
13342
13539
  //#region ../shared-ts/src/utils/errors.ts
@@ -55,6 +55,9 @@ function isEmailOtpWalletRegistrationActivateSuccessV2(result) {
55
55
  function isPasskeyWalletRegistrationActivateSuccessV2(result) {
56
56
  return result.authMethod.kind === "passkey";
57
57
  }
58
+ function isPasskeyWalletRegistrationNearProvisioningSuccessV2(result) {
59
+ return result.authMethod.kind === "passkey";
60
+ }
58
61
  function walletRegistrationRoutePolicyServices(input) {
59
62
  const service = input.services.walletRegistration;
60
63
  return {
@@ -1628,13 +1631,62 @@ async function handleRouterApiWalletRegistrationRespond(input) {
1628
1631
  if (!session) return routeError(500, "internal", "wallet registration respond requires session signing");
1629
1632
  const parsed = parseWalletRegistrationRespondRequest(input.body);
1630
1633
  if (!parsed.ok) return routeError(400, parsed.code, parsed.message);
1634
+ const serverTiming = [];
1631
1635
  const result = await input.services.walletRegistration.respondWalletRegistration({
1632
1636
  ...parsed.value,
1633
1637
  verifier: session,
1634
1638
  ...registrationUserAgentFromHeaders(input.headers) ? { userAgent: registrationUserAgentFromHeaders(input.headers) } : {}
1635
- }, traceContext.value ?? void 0);
1639
+ }, traceContext.value ?? void 0, serverTiming);
1640
+ return routeJson(result.ok ? 200 : 400, result, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
1641
+ }
1642
+ async function handleRouterApiWalletRegistrationNearAdmission(input) {
1643
+ const traceContext = parseRegistrationTraceContext(input.headers);
1644
+ if (!traceContext.ok) return routeError(400, "invalid_trace_id", traceContext.message);
1645
+ if (!isPlainObject(input.body)) return routeError(400, "invalid_body", "JSON body required");
1646
+ const session = input.services.session;
1647
+ if (!session) return routeError(500, "internal", "NEAR admission requires setup verification");
1648
+ const body = input.body;
1649
+ const registrationCeremonyId = String(body.registrationCeremonyId || "").trim();
1650
+ const signedSetup = String(body.signedSetup || "").trim();
1651
+ if (!registrationCeremonyId || !signedSetup) return routeError(400, "invalid_body", "registrationCeremonyId and signedSetup are required");
1652
+ const authority = parseWalletRegistrationAuthorityProof(body);
1653
+ if (!authority.ok) return routeError(400, authority.code, authority.message);
1654
+ const result = await input.services.walletRegistration.authorizeWalletRegistrationNearAdmission({
1655
+ registrationCeremonyId,
1656
+ signedSetup,
1657
+ authority: authority.value,
1658
+ verifier: session,
1659
+ ...registrationUserAgentFromHeaders(input.headers) ? { userAgent: registrationUserAgentFromHeaders(input.headers) } : {}
1660
+ });
1636
1661
  return routeJson(result.ok ? 200 : 400, result);
1637
1662
  }
1663
+ function parseWalletRegistrationAuthorityProof(body) {
1664
+ const hasWebAuthn = Object.prototype.hasOwnProperty.call(body, "webauthn_registration");
1665
+ if (hasWebAuthn === Object.prototype.hasOwnProperty.call(body, "emailOtpRegistrationProof")) return {
1666
+ ok: false,
1667
+ code: "invalid_body",
1668
+ message: "exactly one registration authority proof is required"
1669
+ };
1670
+ if (hasWebAuthn) return {
1671
+ ok: true,
1672
+ value: {
1673
+ kind: "passkey",
1674
+ webauthnRegistration: body.webauthn_registration
1675
+ }
1676
+ };
1677
+ const proof = normalizeEmailOtpRegistrationProof(body.emailOtpRegistrationProof);
1678
+ return proof ? {
1679
+ ok: true,
1680
+ value: {
1681
+ kind: "email_otp",
1682
+ emailOtpRegistrationProof: proof
1683
+ }
1684
+ } : {
1685
+ ok: false,
1686
+ code: "invalid_body",
1687
+ message: "emailOtpRegistrationProof is invalid"
1688
+ };
1689
+ }
1638
1690
  /**
1639
1691
  * Parses respond's public body. `signedSetup` stays an opaque string here —
1640
1692
  * the service verifies it; parsing its contents at the boundary would invite
@@ -1669,29 +1721,9 @@ function parseWalletRegistrationRespondRequest(body) {
1669
1721
  code: "invalid_body",
1670
1722
  message: "an Ed25519-only respond carries no ECDSA registration"
1671
1723
  };
1672
- const hasWebAuthn = Object.prototype.hasOwnProperty.call(body, "webauthn_registration");
1673
- if (hasWebAuthn === Object.prototype.hasOwnProperty.call(body, "emailOtpRegistrationProof")) return {
1674
- ok: false,
1675
- code: "invalid_body",
1676
- message: "exactly one registration authority proof is required"
1677
- };
1678
- let authority;
1679
- if (hasWebAuthn) authority = {
1680
- kind: "passkey",
1681
- webauthnRegistration: body.webauthn_registration
1682
- };
1683
- else {
1684
- const proof = normalizeEmailOtpRegistrationProof(body.emailOtpRegistrationProof);
1685
- if (!proof) return {
1686
- ok: false,
1687
- code: "invalid_body",
1688
- message: "emailOtpRegistrationProof is invalid"
1689
- };
1690
- authority = {
1691
- kind: "email_otp",
1692
- emailOtpRegistrationProof: proof
1693
- };
1694
- }
1724
+ const parsedAuthority = parseWalletRegistrationAuthorityProof(body);
1725
+ if (!parsedAuthority.ok) return parsedAuthority;
1726
+ const authority = parsedAuthority.value;
1695
1727
  if (planKind === "near_ed25519") return {
1696
1728
  ok: true,
1697
1729
  value: {
@@ -1818,6 +1850,7 @@ async function handleRouterApiWalletRegistrationActivate(input) {
1818
1850
  } catch {
1819
1851
  return routeError(400, "invalid_body", "ECDSA activation facts are invalid");
1820
1852
  }
1853
+ const serverTiming = [];
1821
1854
  const result = await input.services.walletRegistration.activateWalletRegistration({
1822
1855
  registrationCeremonyId,
1823
1856
  signedSetup,
@@ -1827,7 +1860,7 @@ async function handleRouterApiWalletRegistrationActivate(input) {
1827
1860
  ...body.emailOtpEnrollment ? { emailOtpEnrollment: body.emailOtpEnrollment } : {},
1828
1861
  ...body.walletCustodyCommit !== void 0 ? { walletCustodyCommit: body.walletCustodyCommit } : {},
1829
1862
  verifier: session
1830
- }, traceContext.value ?? void 0);
1863
+ }, traceContext.value ?? void 0, serverTiming);
1831
1864
  if (!result.ok) return routeJson(400, result);
1832
1865
  if (!isEmailOtpWalletRegistrationActivateSuccessV2(result) && !isPasskeyWalletRegistrationActivateSuccessV2(result)) return routeError(500, "internal", "Wallet registration activation returned an invalid authority");
1833
1866
  const projectionError = await projectActivatedWallet({
@@ -1836,7 +1869,7 @@ async function handleRouterApiWalletRegistrationActivate(input) {
1836
1869
  signedSetup
1837
1870
  });
1838
1871
  if (projectionError) return routeError(500, "internal", `Wallet was created but its Console projection failed: ${projectionError}`);
1839
- return routeJson(200, result);
1872
+ return routeJson(200, result, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
1840
1873
  }
1841
1874
  /**
1842
1875
  * Refactor 94C. `POST /wallets/register/near-provisioning` — the non-blocking
@@ -1854,6 +1887,9 @@ async function handleRouterApiWalletRegistrationNearProvisioning(input) {
1854
1887
  if (!registrationCeremonyId || !signedSetup || !idempotencyKey) return routeError(400, "invalid_body", "registrationCeremonyId, signedSetup and idempotencyKey are required");
1855
1888
  const ed25519 = parseWalletRegistrationEd25519Finalize(body.ed25519);
1856
1889
  if (!ed25519.ok) return routeError(400, ed25519.code, ed25519.message);
1890
+ const sessionSeal = parseRegistrationPreparedSessionSeal(body.sessionSeal);
1891
+ if (!sessionSeal.ok) return routeError(400, sessionSeal.code, sessionSeal.message);
1892
+ const serverTiming = [];
1857
1893
  const result = await input.services.walletRegistration.completeWalletRegistrationNearProvisioning({
1858
1894
  authorization: registrationContinuationAuthorization(input.headers),
1859
1895
  registrationCeremonyId,
@@ -1863,9 +1899,110 @@ async function handleRouterApiWalletRegistrationNearProvisioning(input) {
1863
1899
  emailOtpEnrollment: body.emailOtpEnrollment,
1864
1900
  ...body.walletCustodyCommit !== void 0 ? { walletCustodyCommit: body.walletCustodyCommit } : {},
1865
1901
  verifier: session
1866
- });
1902
+ }, serverTiming);
1867
1903
  if (!result.ok) return routeJson(400, result);
1868
- return routeJson(200, result);
1904
+ if (sessionSeal.value) {
1905
+ if (!isPasskeyWalletRegistrationNearProvisioningSuccessV2(result)) return routeError(400, "invalid_body", "Registration session sealing requires a passkey authority");
1906
+ const sealStartedAt = Date.now();
1907
+ const sealed = await applyRegistrationPreparedSessionSeal({
1908
+ options: input.signingSessionSeal,
1909
+ request: sessionSeal.value,
1910
+ response: result
1911
+ });
1912
+ serverTiming.push(["near_finalize_session_seal", Math.max(0, Date.now() - sealStartedAt)]);
1913
+ if (!sealed.ok) return routeJson(503, {
1914
+ ok: false,
1915
+ code: sealed.code,
1916
+ message: sealed.message
1917
+ }, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
1918
+ return routeJson(200, {
1919
+ ...result,
1920
+ sessionSeal: sealed.value
1921
+ }, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
1922
+ }
1923
+ return routeJson(200, result, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
1924
+ }
1925
+ function parseRegistrationPreparedSessionSeal(raw) {
1926
+ if (raw === void 0) return {
1927
+ ok: true,
1928
+ value: void 0
1929
+ };
1930
+ if (!isPlainObject(raw)) return {
1931
+ ok: false,
1932
+ code: "invalid_body",
1933
+ message: "sessionSeal must be an object"
1934
+ };
1935
+ if (Object.keys(raw).some((field) => ![
1936
+ "thresholdSessionId",
1937
+ "ciphertext",
1938
+ "keyVersion"
1939
+ ].includes(field))) return {
1940
+ ok: false,
1941
+ code: "invalid_body",
1942
+ message: "sessionSeal has unknown fields"
1943
+ };
1944
+ const thresholdSessionId = String(raw.thresholdSessionId || "").trim();
1945
+ const ciphertext = String(raw.ciphertext || "").trim();
1946
+ const keyVersion = String(raw.keyVersion || "").trim();
1947
+ if (!thresholdSessionId || !ciphertext) return {
1948
+ ok: false,
1949
+ code: "invalid_body",
1950
+ message: "sessionSeal thresholdSessionId and ciphertext are required"
1951
+ };
1952
+ return {
1953
+ ok: true,
1954
+ value: {
1955
+ thresholdSessionId,
1956
+ ciphertext,
1957
+ ...keyVersion ? { keyVersion } : {}
1958
+ }
1959
+ };
1960
+ }
1961
+ async function applyRegistrationPreparedSessionSeal(args) {
1962
+ if (!args.options) return {
1963
+ ok: false,
1964
+ code: "signing_session_seal_unavailable",
1965
+ message: "Signing-session sealing is unavailable"
1966
+ };
1967
+ const session = args.response.registrationEstablishedSession.session;
1968
+ const tokens = session.tokens;
1969
+ const ed25519 = tokens.kind === "near_ed25519" || tokens.kind === "near_ed25519_and_evm_family_ecdsa" ? tokens.ed25519 : null;
1970
+ if (!ed25519 || String(ed25519.thresholdSessionId) !== args.request.thresholdSessionId) return {
1971
+ ok: false,
1972
+ code: "invalid_session_seal",
1973
+ message: "Prepared seal does not match the established Ed25519 session"
1974
+ };
1975
+ const sealed = await args.options.service.applyServerSeal(args.request, {
1976
+ userId: String(session.walletId),
1977
+ session: {
1978
+ kind: "exact_wallet_session_operation_credential",
1979
+ curve: "ed25519",
1980
+ thresholdSessionId: args.request.thresholdSessionId,
1981
+ userId: String(session.walletId),
1982
+ expiresAtMs: session.expiresAtMs
1983
+ }
1984
+ });
1985
+ if (!sealed.ok) return sealed;
1986
+ const keyVersion = String(sealed.keyVersion || "").trim();
1987
+ if (!keyVersion) return {
1988
+ ok: false,
1989
+ code: "invalid_session_seal",
1990
+ message: "Signing-session seal response is missing its key version"
1991
+ };
1992
+ return {
1993
+ ok: true,
1994
+ value: {
1995
+ ciphertext: sealed.ciphertext,
1996
+ keyVersion,
1997
+ expiresAtMs: Math.min(sealed.expiresAtMs ?? session.expiresAtMs, session.expiresAtMs),
1998
+ remainingUses: session.remainingUses
1999
+ }
2000
+ };
2001
+ }
2002
+ function ecdsaGatewayServerTimingHeaders(serverTiming) {
2003
+ if (serverTiming.length === 0) return void 0;
2004
+ const header = serverTiming.map(([name, durationMs]) => `${name};dur=${Math.max(0, durationMs).toFixed(1)}`).join(", ");
2005
+ return { "Server-Timing": header };
1869
2006
  }
1870
2007
  async function handleRouterApiWalletAddSignerStart(input) {
1871
2008
  if (!isPlainObject(input.body)) return routeError(400, "invalid_body", "JSON body required");
@@ -2205,6 +2342,6 @@ function registrationContinuationAuthorization(headers) {
2205
2342
  } : { kind: "registration_grant" };
2206
2343
  }
2207
2344
  //#endregion
2208
- export { handleRouterApiWalletAddAuthMethodEmailOtpChallenge, handleRouterApiWalletAddAuthMethodFinalize, handleRouterApiWalletAddAuthMethodIntent, handleRouterApiWalletAddAuthMethodStart, handleRouterApiWalletAddSignerEcdsaActivation, handleRouterApiWalletAddSignerEcdsaDerivationRespond, handleRouterApiWalletAddSignerFinalize, handleRouterApiWalletAddSignerIntent, handleRouterApiWalletAddSignerStart, handleRouterApiWalletEcdsaKeyFactsInventory, handleRouterApiWalletNearImplicitAccountFund, handleRouterApiWalletRegistrationActivate, handleRouterApiWalletRegistrationNearProvisioning, handleRouterApiWalletRegistrationRespond, handleRouterApiWalletRegistrationSetup, handleRouterApiWalletRevokeAuthMethod };
2345
+ export { handleRouterApiWalletAddAuthMethodEmailOtpChallenge, handleRouterApiWalletAddAuthMethodFinalize, handleRouterApiWalletAddAuthMethodIntent, handleRouterApiWalletAddAuthMethodStart, handleRouterApiWalletAddSignerEcdsaActivation, handleRouterApiWalletAddSignerEcdsaDerivationRespond, handleRouterApiWalletAddSignerFinalize, handleRouterApiWalletAddSignerIntent, handleRouterApiWalletAddSignerStart, handleRouterApiWalletEcdsaKeyFactsInventory, handleRouterApiWalletNearImplicitAccountFund, handleRouterApiWalletRegistrationActivate, handleRouterApiWalletRegistrationNearAdmission, handleRouterApiWalletRegistrationNearProvisioning, handleRouterApiWalletRegistrationRespond, handleRouterApiWalletRegistrationSetup, handleRouterApiWalletRevokeAuthMethod };
2209
2346
 
2210
2347
  //# sourceMappingURL=walletRegistrationRoutes.js.map