@learncard/lca-api-service 1.4.0 → 1.5.0

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 (188) hide show
  1. package/.env.example +73 -0
  2. package/AGENTS.md +106 -10
  3. package/CHANGELOG.md +43 -0
  4. package/bootstrapLambda.test.ts +76 -0
  5. package/config/config.dev.json +23 -0
  6. package/config/config.example.json +22 -0
  7. package/config/config.json +1 -0
  8. package/config/config.production.json +12 -0
  9. package/config/config.scouts.dev.json +11 -0
  10. package/config/config.scouts.production.json +11 -0
  11. package/dist/app.d.ts +67 -3
  12. package/dist/cache/getDel.d.ts +2 -0
  13. package/dist/cache/getDel.test.d.ts +1 -0
  14. package/dist/cache/login-tickets.d.ts +12 -0
  15. package/dist/config/applyDockerStageConfig.d.ts +1 -0
  16. package/dist/config/cacheEnvironment.d.ts +16 -0
  17. package/dist/config/environment.d.ts +101 -0
  18. package/dist/config/escrowBundleMode.test.d.ts +1 -0
  19. package/dist/config/escrowDeployment.test.d.ts +1 -0
  20. package/dist/config/oidcEnvironment.d.ts +74 -0
  21. package/dist/config/serverlessFunctionEnv.test.d.ts +1 -0
  22. package/dist/config/stageConfig.d.ts +62 -0
  23. package/dist/config/stageConfig.test.d.ts +1 -0
  24. package/dist/didkit_wasm_bg.wasm +0 -0
  25. package/dist/docker-entry.d.ts +1 -1
  26. package/dist/helpers/auth.helpers.d.ts +1 -1
  27. package/dist/helpers/escrowCancelLink.d.ts +7 -0
  28. package/dist/helpers/escrowCancelLink.test.d.ts +1 -0
  29. package/dist/helpers/escrowCancelToken.d.ts +4 -0
  30. package/dist/helpers/escrowCancelToken.test.d.ts +1 -0
  31. package/dist/helpers/keycloak.helpers.d.ts +23 -0
  32. package/dist/helpers/login-ticket.helpers.d.ts +31 -0
  33. package/dist/helpers/login-ticket.helpers.test.d.ts +1 -0
  34. package/dist/helpers/oidc.helpers.d.ts +47 -0
  35. package/dist/helpers/rate-limit.helpers.d.ts +6 -0
  36. package/dist/helpers/rate-limit.helpers.test.d.ts +1 -0
  37. package/dist/helpers/social-token.helpers.d.ts +19 -0
  38. package/dist/index.d.ts +1 -0
  39. package/dist/index.js +2 -2
  40. package/dist/jobs/escrowBlobRewrap.d.ts +9 -0
  41. package/dist/jobs/escrowHoldReminders.d.ts +12 -0
  42. package/dist/models/AuthSubject.d.ts +20 -0
  43. package/dist/models/EscrowHold.d.ts +79 -1
  44. package/dist/models/Notification.d.ts +5 -0
  45. package/dist/models/UserKey.d.ts +11 -1
  46. package/dist/models/index.d.ts +1 -0
  47. package/dist/oidc.d.ts +5 -0
  48. package/dist/routes/auth.d.ts +31 -0
  49. package/dist/routes/escrow.d.ts +18 -4
  50. package/dist/routes/keys.d.ts +1 -0
  51. package/dist/routes/notifications.d.ts +5 -0
  52. package/dist/routes/signing-authority.d.ts +14 -0
  53. package/dist/services/escrow-enclave/blobIdentity.d.ts +10 -0
  54. package/dist/services/escrow-enclave/blobIdentity.test.d.ts +1 -0
  55. package/dist/services/escrow-enclave/cancellation.d.ts +3 -0
  56. package/dist/services/escrow-enclave/enclaveTrustAnchors.d.ts +1 -0
  57. package/dist/services/escrow-enclave/index.d.ts +16 -0
  58. package/dist/services/escrow-enclave/notifications.d.ts +19 -4
  59. package/dist/services/escrow-enclave/notifications.test.d.ts +1 -0
  60. package/dist/services/escrow-enclave/production.test.d.ts +1 -0
  61. package/dist/services/escrow-enclave/remoteEnclave.contract.test.d.ts +1 -0
  62. package/dist/services/escrow-enclave/remoteEnclave.d.ts +20 -0
  63. package/dist/services/escrow-enclave/remoteEnclave.test.d.ts +1 -0
  64. package/dist/services/escrow-enclave/remoteEnclave.tls.test.d.ts +1 -0
  65. package/dist/services/escrow-enclave/softwareEnclave.d.ts +12 -2
  66. package/dist/services/escrow-enclave/types.d.ts +52 -8
  67. package/dist/types/notifications.d.ts +5 -0
  68. package/esbuildPlugins.cjs +1 -0
  69. package/lambda.ts +27 -117
  70. package/lambdaApp.ts +146 -0
  71. package/oidcLambda.test.ts +38 -0
  72. package/oidcLambda.ts +18 -0
  73. package/oidcLambdaApp.ts +11 -0
  74. package/package.json +23 -13
  75. package/scripts/README.md +88 -0
  76. package/scripts/check-seed-encryption-infrastructure.mjs +18 -2
  77. package/scripts/escrow-blob-mode-report.test.ts +227 -0
  78. package/scripts/escrow-blob-mode-report.ts +465 -0
  79. package/scripts/keycloak-admin.ts +108 -0
  80. package/scripts/keycloak-provider-links.ts +147 -0
  81. package/scripts/print-seed-rollout-flags.ts +21 -0
  82. package/scripts/provision-keycloak-users.ts +419 -0
  83. package/seedMigrationApp.test.ts +145 -0
  84. package/seedMigrationApp.ts +50 -0
  85. package/seedMigrationLambda.test.ts +30 -135
  86. package/seedMigrationLambda.ts +15 -46
  87. package/serverless-local.yml +25 -1
  88. package/serverless.function-env.cjs +58 -0
  89. package/serverless.yml +172 -37
  90. package/src/accesslayer/notifications/index.ts +9 -2
  91. package/src/accesslayer/signing-authority/index.ts +4 -1
  92. package/src/app.ts +2 -0
  93. package/src/cache/getDel.test.ts +34 -0
  94. package/src/cache/getDel.ts +32 -0
  95. package/src/cache/index.ts +1 -1
  96. package/src/cache/login-tickets.ts +26 -0
  97. package/src/config/applyDockerStageConfig.ts +6 -0
  98. package/src/config/cacheEnvironment.ts +34 -0
  99. package/src/config/environment.test.ts +45 -2
  100. package/src/config/environment.ts +90 -23
  101. package/src/config/escrowBundleMode.test.ts +52 -0
  102. package/src/config/escrowDeployment.test.ts +114 -0
  103. package/src/config/oidcEnvironment.ts +59 -0
  104. package/src/config/serverlessFunctionEnv.test.ts +98 -0
  105. package/src/config/stageConfig.test.ts +26 -0
  106. package/src/config/stageConfig.ts +24 -0
  107. package/src/docker-entry.ts +9 -0
  108. package/src/helpers/auth.helpers.ts +68 -30
  109. package/src/helpers/escrowCancelLink.test.ts +60 -0
  110. package/src/helpers/escrowCancelLink.ts +38 -0
  111. package/src/helpers/escrowCancelToken.test.ts +105 -0
  112. package/src/helpers/escrowCancelToken.ts +47 -0
  113. package/src/helpers/keycloak.helpers.ts +120 -0
  114. package/src/helpers/login-ticket.helpers.test.ts +45 -0
  115. package/src/helpers/login-ticket.helpers.ts +91 -0
  116. package/src/helpers/oidc.helpers.ts +354 -0
  117. package/src/helpers/rate-limit.helpers.test.ts +61 -0
  118. package/src/helpers/rate-limit.helpers.ts +73 -0
  119. package/src/helpers/social-token.helpers.ts +144 -0
  120. package/src/index.ts +1 -0
  121. package/src/jobs/escrowBlobRewrap.ts +121 -0
  122. package/src/jobs/escrowHoldReminders.ts +177 -0
  123. package/src/models/AuthSubject.ts +147 -0
  124. package/src/models/EscrowHold.ts +321 -5
  125. package/src/models/UserKey.ts +91 -6
  126. package/src/models/index.ts +1 -0
  127. package/src/oidc.ts +371 -0
  128. package/src/routes/auth.ts +173 -0
  129. package/src/routes/escrow.ts +281 -34
  130. package/src/routes/keys.ts +5 -0
  131. package/src/routes/signing-authority.ts +39 -1
  132. package/src/services/delivery/adapters/postmark.adapter.ts +43 -1
  133. package/src/services/escrow-enclave/README.md +81 -10
  134. package/src/services/escrow-enclave/__fixtures__/tls/pinned-ca.pem +11 -0
  135. package/src/services/escrow-enclave/__fixtures__/tls/pinned-leaf.key +5 -0
  136. package/src/services/escrow-enclave/__fixtures__/tls/pinned-leaf.pem +12 -0
  137. package/src/services/escrow-enclave/__fixtures__/tls/rogue-ca.pem +11 -0
  138. package/src/services/escrow-enclave/__fixtures__/tls/rogue-leaf.key +5 -0
  139. package/src/services/escrow-enclave/__fixtures__/tls/rogue-leaf.pem +12 -0
  140. package/src/services/escrow-enclave/blobIdentity.test.ts +42 -0
  141. package/src/services/escrow-enclave/blobIdentity.ts +55 -0
  142. package/src/services/escrow-enclave/cancellation.ts +59 -0
  143. package/src/services/escrow-enclave/enclaveTrustAnchors.ts +34 -0
  144. package/src/services/escrow-enclave/index.ts +145 -7
  145. package/src/services/escrow-enclave/notifications.test.ts +377 -0
  146. package/src/services/escrow-enclave/notifications.ts +340 -6
  147. package/src/services/escrow-enclave/production.test.ts +34 -0
  148. package/src/services/escrow-enclave/remoteEnclave.contract.test.ts +344 -0
  149. package/src/services/escrow-enclave/remoteEnclave.test.ts +490 -0
  150. package/src/services/escrow-enclave/remoteEnclave.tls.test.ts +62 -0
  151. package/src/services/escrow-enclave/remoteEnclave.ts +263 -0
  152. package/src/services/escrow-enclave/softwareEnclave.test.ts +231 -30
  153. package/src/services/escrow-enclave/softwareEnclave.ts +97 -11
  154. package/src/services/escrow-enclave/types.ts +63 -17
  155. package/test/auth-tickets.spec.ts +480 -0
  156. package/test/helpers/keycloak-broker.ts +181 -0
  157. package/test/helpers/live-mongo.ts +9 -0
  158. package/test/jobs/escrowBlobRewrap.spec.ts +262 -0
  159. package/test/jobs/escrowHoldReminders.spec.ts +319 -0
  160. package/test/keycloak-admin.spec.ts +58 -0
  161. package/test/keycloak-broker-roundtrip.integration.spec.ts +40 -0
  162. package/test/keycloak-migration.integration.spec.ts +266 -0
  163. package/test/keycloak-provider-links.spec.ts +201 -0
  164. package/test/keycloak-social-broker.integration.spec.ts +228 -0
  165. package/test/keycloak-verify.integration.spec.ts +100 -0
  166. package/test/keycloak-verify.spec.ts +307 -0
  167. package/test/keys-escrow.spec.ts +963 -24
  168. package/test/models/authSubjectIndexes.spec.ts +132 -0
  169. package/test/models/escrow.spec.ts +375 -13
  170. package/test/notifications.spec.ts +4 -0
  171. package/test/oidc-e2e/broker.spec.ts +116 -0
  172. package/test/oidc-e2e/compose.yaml +33 -0
  173. package/test/oidc-e2e/fixtures.ts +245 -0
  174. package/test/oidc-e2e/lock.test.mjs +73 -0
  175. package/test/oidc-e2e/playwright.config.ts +21 -0
  176. package/test/oidc-e2e/run.mjs +312 -0
  177. package/test/oidc-e2e/tsconfig.json +10 -0
  178. package/test/oidc-e2e/types.ts +21 -0
  179. package/test/oidc.integration.spec.ts +54 -0
  180. package/test/oidc.spec.ts +697 -0
  181. package/test/oidcLambdaApp.boot.spec.ts +105 -0
  182. package/test/provision-keycloak-users.spec.ts +299 -0
  183. package/test/signing-authority.spec.ts +50 -0
  184. package/tsconfig.build.json +1 -1
  185. package/tsconfig.json +1 -0
  186. package/vitest.config.ts +24 -1
  187. package/vitest.integration.config.ts +20 -3
  188. package/serverless.escrow-env.cjs +0 -20
@@ -4,7 +4,48 @@ import { type AuthProviderMapping } from './UserKey';
4
4
  export declare const ESCROW_HOLDS_COLLECTION = "escrowholds";
5
5
  export declare const ESCROW_HOLD_STALE_WINDOW_MS: number;
6
6
  export declare const ESCROW_HOLD_RESTART_MIN_AGE_MS: number;
7
+ export declare const ESCROW_HOLD_REMINDER_WINDOW_MS: number;
8
+ export declare const EscrowHoldRecordValidator: z.ZodObject<{
9
+ hold: z.ZodObject<{
10
+ holdId: z.ZodString;
11
+ did: z.ZodString;
12
+ shareVersion: z.ZodNumber;
13
+ blobHash: z.ZodString;
14
+ enrollmentEpoch: z.ZodNumber;
15
+ releasePolicy: z.ZodEnum<{
16
+ pin: "pin";
17
+ hold: "hold";
18
+ }>;
19
+ clientEphemeralPublicKey: z.ZodString;
20
+ createdLo: z.ZodNumber;
21
+ createdHi: z.ZodNumber;
22
+ policyVersion: z.ZodNumber;
23
+ signature: z.ZodString;
24
+ }, z.core.$loose>;
25
+ holdDurationMs: z.ZodNumber;
26
+ ledgerSeq: z.ZodNumber;
27
+ }, z.core.$loose>;
7
28
  export declare const EscrowHoldValidator: z.ZodObject<{
29
+ holdRecord: z.ZodObject<{
30
+ hold: z.ZodObject<{
31
+ holdId: z.ZodString;
32
+ did: z.ZodString;
33
+ shareVersion: z.ZodNumber;
34
+ blobHash: z.ZodString;
35
+ enrollmentEpoch: z.ZodNumber;
36
+ releasePolicy: z.ZodEnum<{
37
+ pin: "pin";
38
+ hold: "hold";
39
+ }>;
40
+ clientEphemeralPublicKey: z.ZodString;
41
+ createdLo: z.ZodNumber;
42
+ createdHi: z.ZodNumber;
43
+ policyVersion: z.ZodNumber;
44
+ signature: z.ZodString;
45
+ }, z.core.$loose>;
46
+ holdDurationMs: z.ZodNumber;
47
+ ledgerSeq: z.ZodNumber;
48
+ }, z.core.$loose>;
8
49
  _id: z.ZodString;
9
50
  authProvider: z.ZodObject<{
10
51
  type: z.ZodEnum<{
@@ -40,36 +81,73 @@ export declare const EscrowHoldValidator: z.ZodObject<{
40
81
  "release-failed": "release-failed";
41
82
  }>>;
42
83
  cancelledAt: z.ZodOptional<z.ZodDate>;
84
+ enclaveCancelPendingAt: z.ZodOptional<z.ZodDate>;
85
+ enclaveCancelConfirmedAt: z.ZodOptional<z.ZodDate>;
86
+ enclaveCancelAttempts: z.ZodOptional<z.ZodNumber>;
87
+ enclaveEnvelope: z.ZodOptional<z.ZodObject<{
88
+ version: z.ZodLiteral<1>;
89
+ algorithm: z.ZodLiteral<"P-256-HKDF-SHA256-AES-256-GCM">;
90
+ keyId: z.ZodString;
91
+ ephemeralPublicKey: z.ZodString;
92
+ salt: z.ZodString;
93
+ iv: z.ZodString;
94
+ ciphertext: z.ZodString;
95
+ }, z.core.$strict>>;
43
96
  cancelledBy: z.ZodOptional<z.ZodEnum<{
44
97
  did: "did";
98
+ link: "link";
45
99
  system: "system";
46
100
  }>>;
47
101
  completedAt: z.ZodOptional<z.ZodDate>;
48
102
  clientEphemeralPublicKey: z.ZodString;
49
103
  resumeTokenHash: z.ZodString;
104
+ cancelTokenHash: z.ZodOptional<z.ZodString>;
105
+ cancelTokenUsedAt: z.ZodOptional<z.ZodDate>;
106
+ cancelTokenHashes: z.ZodOptional<z.ZodArray<z.ZodString>>;
107
+ startedNotificationPending: z.ZodOptional<z.ZodBoolean>;
108
+ reminderClaimedUntil: z.ZodOptional<z.ZodDate>;
109
+ startedNotificationAttemptedAt: z.ZodOptional<z.ZodDate>;
110
+ reminderAttemptedAt: z.ZodOptional<z.ZodDate>;
50
111
  notifications: z.ZodDefault<z.ZodArray<z.ZodObject<{
51
112
  kind: z.ZodEnum<{
52
113
  cancelled: "cancelled";
53
114
  completed: "completed";
115
+ "pin-locked": "pin-locked";
54
116
  started: "started";
117
+ reminder: "reminder";
55
118
  }>;
56
119
  sentAt: z.ZodDate;
57
120
  }, z.core.$strip>>>;
58
121
  requestIp: z.ZodOptional<z.ZodString>;
122
+ tenantId: z.ZodOptional<z.ZodString>;
59
123
  createdAt: z.ZodDate;
60
124
  updatedAt: z.ZodDate;
61
125
  }, z.core.$strip>;
62
126
  export type EscrowHold = z.infer<typeof EscrowHoldValidator>;
63
- export type CreateEscrowHoldInput = Omit<z.input<typeof EscrowHoldValidator>, '_id' | 'status' | 'createdAt' | 'updatedAt' | 'notifications' | 'cancelledAt' | 'cancelledBy' | 'completedAt' | 'cancelReason'>;
127
+ export type CreateEscrowHoldInput = Omit<z.input<typeof EscrowHoldValidator>, '_id' | 'status' | 'createdAt' | 'updatedAt' | 'notifications' | 'cancelledAt' | 'cancelledBy' | 'completedAt' | 'cancelReason' | 'cancelTokenUsedAt' | 'enclaveCancelPendingAt' | 'enclaveCancelConfirmedAt' | 'enclaveCancelAttempts'>;
64
128
  export declare const getEscrowHoldsCollection: () => Collection<EscrowHold>;
65
129
  export declare const createEscrowHoldsIndexes: () => Promise<void>;
66
130
  export declare const createEscrowHold: (input: CreateEscrowHoldInput) => Promise<EscrowHold>;
67
131
  export declare const findPendingEscrowHoldByAuthProvider: (authProvider: AuthProviderMapping, releasePolicy?: EscrowHold["releasePolicy"]) => Promise<EscrowHold | null>;
68
132
  export declare const findEscrowHoldById: (id: string) => Promise<EscrowHold | null>;
133
+ export declare const hasUnconfirmedEscrowCancellation: (primaryDid: string) => Promise<boolean>;
134
+ export declare const findEscrowHoldsPendingEnclaveCancellation: (limit: number) => Promise<EscrowHold[]>;
135
+ export declare const countEscrowCancellationsNearRelease: (now: Date) => Promise<number>;
136
+ export declare const recordEscrowEnclaveCancelAttempt: (id: string) => Promise<boolean>;
137
+ export declare const confirmEscrowEnclaveCancellation: (id: string) => Promise<void>;
69
138
  export declare const cancelEscrowHold: (id: string, cancelledBy: "did" | "system", cancelReason?: EscrowHold["cancelReason"]) => Promise<EscrowHold | null>;
139
+ export declare const cancelEscrowHoldByCancelToken: (id: string, cancelTokenHash: string) => Promise<EscrowHold | null>;
70
140
  export declare const completeEscrowHold: (id: string) => Promise<EscrowHold | null>;
71
141
  export declare const hasCompletedEscrowHoldForVersion: (authProvider: AuthProviderMapping, shareVersion: number) => Promise<boolean>;
72
142
  export declare const expireStaleEscrowHolds: (now: Date) => Promise<number>;
73
143
  export declare const markClaimedEscrowHoldFailed: (id: string, reason: "pin-mismatch" | "pin-locked" | "release-failed", completedAt: Date) => Promise<void>;
144
+ export declare const retryClaimedEscrowHold: (id: string, completedAt: Date) => Promise<void>;
145
+ export declare const findEscrowHoldsPendingStartNotification: (limit: number) => Promise<EscrowHold[]>;
146
+ export declare const recordEscrowStartNotificationAttempt: (id: string, now: Date) => Promise<void>;
147
+ export declare const findEscrowHoldsDueForReminder: (now: Date, limit: number) => Promise<EscrowHold[]>;
148
+ export declare const claimEscrowHoldForReminder: (id: string, now: Date) => Promise<EscrowHold | null>;
149
+ export declare const releaseEscrowReminderClaim: (hold: EscrowHold) => Promise<void>;
74
150
  export declare const hashEscrowResumeToken: (token: string) => string;
75
151
  export declare const generateEscrowResumeToken: () => string;
152
+ export declare const recordEscrowHoldNotification: (holdId: string, kind: EscrowHold["notifications"][number]["kind"]) => Promise<void>;
153
+ export declare const rotateEscrowCancelToken: (holdId: string) => Promise<string | null>;
@@ -167,6 +167,11 @@ export declare const MongoNotificationValidator: z.ZodObject<{
167
167
  approvedAt: z.ZodString;
168
168
  contractUpdatedAt: z.ZodString;
169
169
  }, z.core.$strip>>;
170
+ referral: z.ZodOptional<z.ZodObject<{
171
+ requestId: z.ZodString;
172
+ requestedBy: z.ZodString;
173
+ externalReferenceId: z.ZodOptional<z.ZodString>;
174
+ }, z.core.$strip>>;
170
175
  id: z.ZodString;
171
176
  action: z.ZodEnum<{
172
177
  write: "write";
@@ -72,6 +72,8 @@ export declare const EscrowEnvelopeValidator: z.ZodObject<{
72
72
  ciphertext: z.ZodString;
73
73
  }, z.core.$strict>;
74
74
  export declare const EscrowBlobValidator: z.ZodObject<{
75
+ enrollmentEpoch: z.ZodNumber;
76
+ blobHash: z.ZodString;
75
77
  envelope: z.ZodObject<{
76
78
  version: z.ZodLiteral<1>;
77
79
  algorithm: z.ZodLiteral<"P-256-HKDF-SHA256-AES-256-GCM">;
@@ -119,6 +121,8 @@ export declare const EscrowPinStatusValidator: z.ZodObject<{
119
121
  export declare const getEscrowPinStatus: (userKey: MongoUserKeyType) => z.infer<typeof EscrowPinStatusValidator>;
120
122
  declare const MongoUserKeyBaseValidator: z.ZodObject<{
121
123
  escrowBlob: z.ZodOptional<z.ZodObject<{
124
+ enrollmentEpoch: z.ZodNumber;
125
+ blobHash: z.ZodString;
122
126
  envelope: z.ZodObject<{
123
127
  version: z.ZodLiteral<1>;
124
128
  algorithm: z.ZodLiteral<"P-256-HKDF-SHA256-AES-256-GCM">;
@@ -235,6 +239,8 @@ declare const MongoUserKeyBaseValidator: z.ZodObject<{
235
239
  }, z.core.$strip>;
236
240
  export declare const MongoUserKeyValidator: z.ZodObject<{
237
241
  escrowBlob: z.ZodOptional<z.ZodObject<{
242
+ enrollmentEpoch: z.ZodNumber;
243
+ blobHash: z.ZodString;
238
244
  envelope: z.ZodObject<{
239
245
  version: z.ZodLiteral<1>;
240
246
  algorithm: z.ZodLiteral<"P-256-HKDF-SHA256-AES-256-GCM">;
@@ -409,6 +415,8 @@ export declare const getUserKeysCollection: () => import("mongodb").Collection<{
409
415
  createdAt: Date;
410
416
  updatedAt: Date;
411
417
  escrowBlob?: {
418
+ enrollmentEpoch: number;
419
+ blobHash: string;
412
420
  envelope: {
413
421
  version: 1;
414
422
  algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
@@ -462,12 +470,14 @@ export declare const upsertUserKey: (contactMethod: ContactMethod, data: Partial
462
470
  export declare const upsertUserKeyByAuthProvider: (contactMethod: ContactMethod, authProvider: AuthProviderMapping, data: Partial<Omit<MongoUserKeyType, "_id" | "contactMethod" | "createdAt" | "authProviders">>, expectedVersion?: number) => Promise<MongoUserKeyType>;
463
471
  export declare const addAuthProviderToUserKey: (contactMethod: ContactMethod, authProvider: AuthProviderMapping) => Promise<void>;
464
472
  export declare const addRecoveryMethodToUserKey: (contactMethod: ContactMethod, recoveryMethod: RecoveryMethod) => Promise<void>;
465
- export declare const setEscrowBlobByAuthProvider: (authProvider: AuthProviderMapping, blob: EscrowBlob, expectedShareVersion: number, pin?: {
473
+ export declare const setEscrowBlobByAuthProvider: (authProvider: AuthProviderMapping, blob: Omit<EscrowBlob, "enrollmentEpoch" | "blobHash">, expectedShareVersion: number, pin?: {
466
474
  salt: string;
467
475
  }, carryFrom?: {
468
476
  blob: EscrowBlob;
469
477
  pin: NonNullable<MongoUserKeyType["escrowPin"]>;
470
478
  }) => Promise<MongoUserKeyType | null>;
479
+ export declare const rewrapEscrowBlobByAuthProvider: (authProvider: AuthProviderMapping, oldBlob: EscrowBlob, oldPin: MongoUserKeyType["escrowPin"], newEnvelope: EscrowBlob["envelope"], identity: Pick<EscrowBlob, "enclaveKeyId" | "enclaveMode" | "measurements">) => Promise<MongoUserKeyType | null>;
480
+ export declare const findUserKeysWithEscrowBlobKeyId: (keyIds: string[], limit: number) => Promise<MongoUserKeyType[]>;
471
481
  export declare const clearEscrowByAuthProvider: (authProvider: AuthProviderMapping, { optOut }: {
472
482
  optOut: boolean;
473
483
  }) => Promise<MongoUserKeyType | null>;
@@ -6,3 +6,4 @@ export * from './Pins';
6
6
  export * from './Preferences';
7
7
  export * from './UserKey';
8
8
  export * from './EscrowHold';
9
+ export * from './AuthSubject';
package/dist/oidc.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { type FastifyPluginAsync } from 'fastify';
2
+ export declare const oidcFastifyPlugin: FastifyPluginAsync;
3
+ export declare const app: import("fastify").FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault> & PromiseLike<import("fastify").FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>> & {
4
+ __linterBrands: "SafePromiseLike";
5
+ };
@@ -0,0 +1,31 @@
1
+ export declare const authRouter: import("@trpc/server").TRPCBuiltRouter<{
2
+ ctx: import("@routes").Context;
3
+ meta: import("trpc-to-openapi").OpenApiMeta;
4
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape;
5
+ transformer: false;
6
+ }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
7
+ requestLoginTicket: import("@trpc/server").TRPCMutationProcedure<{
8
+ input: {
9
+ email: string;
10
+ code: string;
11
+ };
12
+ output: {
13
+ success: boolean;
14
+ ticket?: string | undefined;
15
+ error?: string | undefined;
16
+ };
17
+ meta: import("trpc-to-openapi").OpenApiMeta;
18
+ }>;
19
+ requestSocialLoginTicket: import("@trpc/server").TRPCMutationProcedure<{
20
+ input: {
21
+ provider: "google" | "apple";
22
+ idToken: string;
23
+ };
24
+ output: {
25
+ success: boolean;
26
+ ticket?: string | undefined;
27
+ error?: string | undefined;
28
+ };
29
+ meta: import("trpc-to-openapi").OpenApiMeta;
30
+ }>;
31
+ }>>;
@@ -1,11 +1,14 @@
1
+ import { type Context } from '@routes';
1
2
  export declare const escrowRouter: import("@trpc/server").TRPCBuiltRouter<{
2
- ctx: import("@routes").Context;
3
+ ctx: Context;
3
4
  meta: import("trpc-to-openapi").OpenApiMeta;
4
5
  errorShape: import("@trpc/server").TRPCDefaultErrorShape;
5
6
  transformer: false;
6
7
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
7
8
  getAttestation: import("@trpc/server").TRPCQueryProcedure<{
8
- input: Record<string, never>;
9
+ input: {
10
+ nonce?: string | undefined;
11
+ };
9
12
  output: {
10
13
  attestation: {
11
14
  mode: "software" | "nitro";
@@ -19,6 +22,7 @@ export declare const escrowRouter: import("@trpc/server").TRPCBuiltRouter<{
19
22
  };
20
23
  document: string;
21
24
  issuedAt: string;
25
+ previousKeyIds?: string[] | undefined;
22
26
  };
23
27
  holdDurationMs: number;
24
28
  };
@@ -79,10 +83,10 @@ export declare const escrowRouter: import("@trpc/server").TRPCBuiltRouter<{
79
83
  recoverySessionToken?: string | undefined;
80
84
  };
81
85
  output: {
86
+ holdId: string;
87
+ releasePolicy: "pin" | "hold";
82
88
  requestedAt: string;
83
89
  releaseAfter: string;
84
- releasePolicy: "pin" | "hold";
85
- holdId: string;
86
90
  status: "pending";
87
91
  resumeToken: string | null;
88
92
  pinSalt?: string | undefined;
@@ -120,6 +124,16 @@ export declare const escrowRouter: import("@trpc/server").TRPCBuiltRouter<{
120
124
  };
121
125
  meta: import("trpc-to-openapi").OpenApiMeta;
122
126
  }>;
127
+ cancelRecoveryByLink: import("@trpc/server").TRPCMutationProcedure<{
128
+ input: {
129
+ holdId: string;
130
+ token: string;
131
+ };
132
+ output: {
133
+ cancelled: boolean;
134
+ };
135
+ meta: import("trpc-to-openapi").OpenApiMeta;
136
+ }>;
123
137
  completeRecovery: import("@trpc/server").TRPCMutationProcedure<{
124
138
  input: {
125
139
  holdId: string;
@@ -153,6 +153,7 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
153
153
  salt?: string | undefined;
154
154
  };
155
155
  sssActivationState: "provisional" | "active";
156
+ escrowStale?: "mode-mismatch" | "key-rotated" | undefined;
156
157
  } | null;
157
158
  meta: import("trpc-to-openapi").OpenApiMeta;
158
159
  }>;
@@ -207,6 +207,11 @@ export declare const notificationsRouter: import("@trpc/server").TRPCBuiltRouter
207
207
  approvedAt: string;
208
208
  contractUpdatedAt: string;
209
209
  } | undefined;
210
+ referral?: {
211
+ requestId: string;
212
+ requestedBy: string;
213
+ externalReferenceId?: string | undefined;
214
+ } | undefined;
210
215
  uris?: string[] | undefined;
211
216
  } | undefined;
212
217
  inbox?: {
@@ -18,6 +18,20 @@ export declare const signingAuthorityRouter: import("@trpc/server").TRPCBuiltRou
18
18
  };
19
19
  meta: import("trpc-to-openapi").OpenApiMeta;
20
20
  }>;
21
+ ensureManagedSigningAuthority: import("@trpc/server").TRPCMutationProcedure<{
22
+ input: {
23
+ name: string;
24
+ ownerDid: string;
25
+ };
26
+ output: {
27
+ ownerDid: string;
28
+ name: string;
29
+ did: string;
30
+ endpoint: string;
31
+ _id?: string | undefined;
32
+ };
33
+ meta: import("trpc-to-openapi").OpenApiMeta;
34
+ }>;
21
35
  signingAuthorities: import("@trpc/server").TRPCQueryProcedure<{
22
36
  input: void;
23
37
  output: {
@@ -0,0 +1,10 @@
1
+ export interface EscrowEnvelopeFields {
2
+ version: number;
3
+ algorithm: string;
4
+ keyId: string;
5
+ ephemeralPublicKey: string;
6
+ salt: string;
7
+ iv: string;
8
+ ciphertext: string;
9
+ }
10
+ export declare const escrowBlobIdentity: (envelope: EscrowEnvelopeFields) => string;
@@ -0,0 +1,3 @@
1
+ import { type EscrowHold, type MongoUserKeyType } from '@models';
2
+ export declare const logEscrowCancellationPending: (urgent: boolean) => void;
3
+ export declare const cancelHoldInEnclave: (hold: EscrowHold, userKey?: MongoUserKeyType | null, now?: Date) => Promise<boolean>;
@@ -0,0 +1 @@
1
+ export declare const pinnedEnclaveCa: (baseUrl: string) => string | undefined;
@@ -1,9 +1,25 @@
1
+ import type { EscrowBlobStaleReason } from '@learncard/types';
1
2
  import { type EscrowEnclave } from './types';
2
3
  export * from './types';
3
4
  export * from './softwareEnclave';
5
+ export * from './remoteEnclave';
4
6
  export * from './notifications';
5
7
  export declare const isEscrowEnabled: () => boolean;
8
+ export declare const isEscrowRemoteMode: () => boolean;
9
+ export declare const activeEscrowClientMode: () => "software" | "nitro";
6
10
  export declare const getEscrowHoldDurationMs: () => number;
7
11
  export declare const getEscrowHoldRestartMinAgeMs: () => number;
8
12
  export declare const getEscrowEnclave: () => EscrowEnclave;
13
+ export type { EscrowBlobStaleReason };
14
+ interface EscrowAttestationIdentity {
15
+ keyId: string;
16
+ previousKeyIds: string[];
17
+ mode: 'software' | 'nitro';
18
+ }
19
+ export declare const getEnclaveAttestationIdentity: () => Promise<EscrowAttestationIdentity>;
20
+ export declare const getEscrowBlobStaleReason: (blob: {
21
+ enclaveMode: "software" | "nitro";
22
+ enclaveKeyId: string;
23
+ }) => Promise<EscrowBlobStaleReason | undefined>;
9
24
  export declare const __setEscrowEnclaveForTests: (replacement: EscrowEnclave | undefined) => void;
25
+ export declare const __resetEscrowAttestationCacheForTests: () => void;
@@ -1,9 +1,24 @@
1
- import type { EscrowHold } from '../../models/EscrowHold';
2
- import type { MongoUserKeyType } from '../../models/UserKey';
1
+ import type { ResolvedTenant } from '@learncard/email-templates';
2
+ import type { EscrowHold, MongoUserKeyType } from '@models';
3
+ import { type DeliveryService } from '../delivery';
4
+ export type EscrowHoldEventKind = 'started' | 'reminder' | 'completed' | 'cancelled' | 'pin-locked';
3
5
  export interface EscrowHoldEvent {
4
- kind: 'started' | 'cancelled' | 'completed';
6
+ kind: EscrowHoldEventKind;
5
7
  hold: EscrowHold;
6
8
  userKey: MongoUserKeyType;
7
9
  reason?: 'superseded';
10
+ cancelToken?: string;
11
+ tenant?: ResolvedTenant;
12
+ activeHold?: EscrowHold;
8
13
  }
9
- export declare const notifyEscrowHoldEvent: (event: EscrowHoldEvent) => Promise<void>;
14
+ type SendPushNotification = typeof import('@helpers/pushNotifications.helpers').sendPushNotification;
15
+ export interface EscrowHoldNotifierDeps {
16
+ send: DeliveryService['send'];
17
+ sendPush: SendPushNotification;
18
+ now: () => Date;
19
+ rotateCancelToken: (holdId: string) => Promise<string | null>;
20
+ recordNotification: (holdId: string, kind: EscrowHoldEventKind) => Promise<void>;
21
+ }
22
+ export declare const ESCROW_NOTIFICATION_TIMEOUT_MS = 3000;
23
+ export declare const notifyEscrowHoldEvent: (event: EscrowHoldEvent, overrides?: Partial<EscrowHoldNotifierDeps>) => Promise<boolean>;
24
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import { type EscrowEnclave } from './types';
2
+ export interface RemoteEnclaveTransportResponse {
3
+ status: number;
4
+ data: unknown;
5
+ }
6
+ export interface RemoteEnclaveTransport {
7
+ post(url: string, body: unknown, config: {
8
+ headers: Record<string, string>;
9
+ timeout: number;
10
+ }): Promise<RemoteEnclaveTransportResponse>;
11
+ }
12
+ export declare const createHttpTransport: (caPem?: string) => RemoteEnclaveTransport;
13
+ export interface RemoteEnclaveConfig {
14
+ baseUrl: string;
15
+ token: string;
16
+ timeoutMs: number;
17
+ caPem?: string;
18
+ transport?: RemoteEnclaveTransport;
19
+ }
20
+ export declare const createRemoteEnclave: (config: RemoteEnclaveConfig) => EscrowEnclave;
@@ -1,17 +1,27 @@
1
- import { type EscrowEnclave, type EnclaveAttestation, type VerifyEscrowBlobInput, type VerifyEscrowBlobResult, type ReleaseRequest, type ReleaseResult, type CarryPinVerifierInput } from './types';
1
+ import type { EnclaveCreateHoldInput, EscrowHoldRecord, CancelHoldRequest } from './types';
2
+ import { type EscrowEnclave, type EnclaveAttestation, type VerifyEscrowBlobInput, type VerifyEscrowBlobResult, type ReleaseRequest, type ReleaseResult, type CarryPinVerifierInput, type RewrapEscrowBlobInput } from './types';
2
3
  export interface SoftwareEnclaveConfig {
3
4
  privateKeys: Record<string, string>;
4
5
  activeKeyId: string;
6
+ holdDurationMs?: number;
5
7
  }
6
8
  export declare class SoftwareEnclave implements EscrowEnclave {
7
9
  private readonly privateKeys;
8
10
  private readonly activeKeyId;
11
+ private readonly holdDurationMs;
9
12
  constructor(config: SoftwareEnclaveConfig);
10
- getAttestation(): Promise<EnclaveAttestation>;
13
+ createHold(input: EnclaveCreateHoldInput): Promise<{
14
+ holdRecord: EscrowHoldRecord;
15
+ }>;
16
+ cancelHold(input: CancelHoldRequest): Promise<void>;
17
+ getAttestation(_nonce?: Uint8Array): Promise<EnclaveAttestation>;
11
18
  private decrypt;
12
19
  verifyEscrowBlob(input: VerifyEscrowBlobInput): Promise<VerifyEscrowBlobResult>;
13
20
  carryPinVerifier(input: CarryPinVerifierInput): Promise<{
14
21
  envelope: import("@learncard/sss-key-manager").EscrowEnvelope;
15
22
  }>;
23
+ rewrapEscrowBlob(input: RewrapEscrowBlobInput): Promise<{
24
+ envelope: import("@learncard/sss-key-manager").EscrowEnvelope;
25
+ }>;
16
26
  releaseEscrow(input: ReleaseRequest): Promise<ReleaseResult>;
17
27
  }
@@ -1,8 +1,8 @@
1
1
  import type { EscrowEnvelope } from '@learncard/sss-key-manager';
2
- import type { EscrowHold } from '../../models/EscrowHold';
3
2
  export interface EnclaveAttestation {
4
3
  mode: 'software' | 'nitro';
5
4
  keyId: string;
5
+ previousKeyIds?: string[];
6
6
  publicKey: string;
7
7
  measurements: {
8
8
  imageSha384?: string;
@@ -13,7 +13,40 @@ export interface EnclaveAttestation {
13
13
  document: string;
14
14
  issuedAt: string;
15
15
  }
16
- export type EscrowHoldForEnclave = Pick<EscrowHold, '_id' | 'status' | 'releaseAfter' | 'releasePolicy' | 'primaryDid' | 'shareVersion' | 'clientEphemeralPublicKey'>;
16
+ export interface EscrowHoldRecord {
17
+ [key: string]: unknown;
18
+ hold: {
19
+ [key: string]: unknown;
20
+ holdId: string;
21
+ did: string;
22
+ shareVersion: number;
23
+ blobHash: string;
24
+ enrollmentEpoch: number;
25
+ releasePolicy: 'hold' | 'pin';
26
+ clientEphemeralPublicKey: string;
27
+ createdLo: number;
28
+ createdHi: number;
29
+ policyVersion: number;
30
+ signature: string;
31
+ };
32
+ holdDurationMs: number;
33
+ ledgerSeq: number;
34
+ }
35
+ export interface EnclaveCreateHoldInput {
36
+ envelope: EscrowEnvelope;
37
+ holdId: string;
38
+ expectedDid: string;
39
+ expectedShareVersion: number;
40
+ enrollmentEpoch: number;
41
+ releasePolicy: 'hold' | 'pin';
42
+ clientEphemeralPublicKey: string;
43
+ }
44
+ export interface CancelHoldRequest {
45
+ envelope: EscrowEnvelope;
46
+ hold: EscrowHoldRecord;
47
+ clientEphemeralPublicKey: string;
48
+ expectedDid: string;
49
+ }
17
50
  export interface VerifyEscrowBlobInput {
18
51
  envelope: EscrowEnvelope;
19
52
  expectedDid: string;
@@ -27,11 +60,7 @@ export type VerifyEscrowBlobResult = {
27
60
  hasPin: boolean;
28
61
  reason: string;
29
62
  };
30
- export interface ReleaseRequest {
31
- envelope: EscrowEnvelope;
32
- hold: EscrowHoldForEnclave;
33
- clientEphemeralPublicKey: string;
34
- expectedDid: string;
63
+ export interface ReleaseRequest extends CancelHoldRequest {
35
64
  now?: Date;
36
65
  pinProof?: string;
37
66
  }
@@ -44,14 +73,29 @@ export interface CarryPinVerifierInput {
44
73
  expectedDid: string;
45
74
  sourceShareVersion: number;
46
75
  targetShareVersion: number;
76
+ sourceEnrollmentEpoch: number;
77
+ targetEnrollmentEpoch: number;
78
+ }
79
+ export interface RewrapEscrowBlobInput {
80
+ envelope: EscrowEnvelope;
81
+ expectedDid: string;
82
+ expectedShareVersion: number;
83
+ sourceEnrollmentEpoch: number;
47
84
  }
48
85
  export interface EscrowEnclave {
49
- getAttestation(): Promise<EnclaveAttestation>;
86
+ getAttestation(nonce?: Uint8Array): Promise<EnclaveAttestation>;
50
87
  verifyEscrowBlob(input: VerifyEscrowBlobInput): Promise<VerifyEscrowBlobResult>;
51
88
  carryPinVerifier(input: CarryPinVerifierInput): Promise<{
52
89
  envelope: EscrowEnvelope;
53
90
  }>;
91
+ rewrapEscrowBlob(input: RewrapEscrowBlobInput): Promise<{
92
+ envelope: EscrowEnvelope;
93
+ }>;
54
94
  releaseEscrow(input: ReleaseRequest): Promise<ReleaseResult>;
95
+ createHold(input: EnclaveCreateHoldInput): Promise<{
96
+ holdRecord: EscrowHoldRecord;
97
+ }>;
98
+ cancelHold(input: CancelHoldRequest): Promise<void>;
55
99
  }
56
100
  export declare class EscrowPolicyError extends Error {
57
101
  constructor();
@@ -237,6 +237,11 @@ export declare const PaginatedNotificationsValidator: z.ZodObject<{
237
237
  approvedAt: z.ZodString;
238
238
  contractUpdatedAt: z.ZodString;
239
239
  }, z.core.$strip>>;
240
+ referral: z.ZodOptional<z.ZodObject<{
241
+ requestId: z.ZodString;
242
+ requestedBy: z.ZodString;
243
+ externalReferenceId: z.ZodOptional<z.ZodString>;
244
+ }, z.core.$strip>>;
240
245
  id: z.ZodString;
241
246
  action: z.ZodEnum<{
242
247
  write: "write";
@@ -41,6 +41,7 @@ module.exports = [
41
41
  forceCjsSodiumWrapperPlugin,
42
42
  copyDidkitWasmPlugin,
43
43
  sentryEsbuildPlugin({
44
+ telemetry: process.env.SENTRY_BUILD_TELEMETRY !== 'false',
44
45
  authToken: process.env.SENTRY_AUTH_TOKEN,
45
46
  org: process.env.SENTRY_ORG,
46
47
  project: process.env.SENTRY_PROJECT,