@learncard/network-brain-service 3.17.0 → 3.19.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 (274) hide show
  1. package/.env.example +4 -0
  2. package/AGENTS.md +177 -159
  3. package/CHANGELOG.md +148 -0
  4. package/README.md +26 -8
  5. package/compose.inbox.yml +7 -0
  6. package/dist/accesslayer/app-store-listing/relationships/read.d.ts.map +1 -1
  7. package/dist/accesslayer/auth-grant/read.d.ts +1 -0
  8. package/dist/accesslayer/auth-grant/read.d.ts.map +1 -1
  9. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +3 -2
  10. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
  11. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +1 -0
  12. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
  13. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +5 -3
  14. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
  15. package/dist/accesslayer/credential/create.d.ts +2 -1
  16. package/dist/accesslayer/credential/create.d.ts.map +1 -1
  17. package/dist/accesslayer/credential/create.test.d.ts +2 -0
  18. package/dist/accesslayer/credential/create.test.d.ts.map +1 -0
  19. package/dist/accesslayer/credential/relationships/read.d.ts +1 -0
  20. package/dist/accesslayer/credential/relationships/read.d.ts.map +1 -1
  21. package/dist/accesslayer/credential-activity/create.d.ts.map +1 -1
  22. package/dist/accesslayer/credential-refresh/email-delivery.d.ts +18 -0
  23. package/dist/accesslayer/credential-refresh/email-delivery.d.ts.map +1 -0
  24. package/dist/accesslayer/credential-refresh/index.d.ts +1 -0
  25. package/dist/accesslayer/credential-refresh/index.d.ts.map +1 -1
  26. package/dist/accesslayer/credential-refresh/read.d.ts.map +1 -1
  27. package/dist/accesslayer/credential-refresh/update.d.ts.map +1 -1
  28. package/dist/accesslayer/inbox-batch/store.d.ts +33 -0
  29. package/dist/accesslayer/inbox-batch/store.d.ts.map +1 -0
  30. package/dist/accesslayer/inbox-credential/create.d.ts +5 -0
  31. package/dist/accesslayer/inbox-credential/create.d.ts.map +1 -1
  32. package/dist/accesslayer/inbox-credential/refresh.d.ts +71 -0
  33. package/dist/accesslayer/inbox-credential/refresh.d.ts.map +1 -0
  34. package/dist/accesslayer/inbox-credential/update.d.ts +1 -1
  35. package/dist/accesslayer/inbox-credential/update.d.ts.map +1 -1
  36. package/dist/cache/atomic-counter.test-helpers.d.ts +3 -0
  37. package/dist/cache/atomic-counter.test-helpers.d.ts.map +1 -0
  38. package/dist/cache/atomic-counter.test.d.ts +2 -0
  39. package/dist/cache/atomic-counter.test.d.ts.map +1 -0
  40. package/dist/cache/index.d.ts +1 -1
  41. package/dist/cache/index.d.ts.map +1 -1
  42. package/dist/config/environment.d.ts +46 -0
  43. package/dist/config/environment.d.ts.map +1 -1
  44. package/dist/credential-refresh.d.ts.map +1 -1
  45. package/dist/didkit_wasm_bg.wasm +0 -0
  46. package/dist/helpers/activity.helpers.d.ts +4 -0
  47. package/dist/helpers/activity.helpers.d.ts.map +1 -1
  48. package/dist/helpers/boost-storage.helpers.test.d.ts +2 -0
  49. package/dist/helpers/boost-storage.helpers.test.d.ts.map +1 -0
  50. package/dist/helpers/boost.helpers.d.ts +3 -7
  51. package/dist/helpers/boost.helpers.d.ts.map +1 -1
  52. package/dist/helpers/contact-method.helpers.d.ts.map +1 -1
  53. package/dist/helpers/contact-method.helpers.test.d.ts +2 -0
  54. package/dist/helpers/contact-method.helpers.test.d.ts.map +1 -0
  55. package/dist/helpers/credential-refresh-email-content.helpers.d.ts +44 -0
  56. package/dist/helpers/credential-refresh-email-content.helpers.d.ts.map +1 -0
  57. package/dist/helpers/credential-refresh-email-content.helpers.test.d.ts +2 -0
  58. package/dist/helpers/credential-refresh-email-content.helpers.test.d.ts.map +1 -0
  59. package/dist/helpers/credential-refresh-email.helpers.d.ts +17 -0
  60. package/dist/helpers/credential-refresh-email.helpers.d.ts.map +1 -0
  61. package/dist/helpers/credential-refresh-proof.helpers.d.ts +4 -0
  62. package/dist/helpers/credential-refresh-proof.helpers.d.ts.map +1 -0
  63. package/dist/helpers/credential-refresh-proof.helpers.test.d.ts +2 -0
  64. package/dist/helpers/credential-refresh-proof.helpers.test.d.ts.map +1 -0
  65. package/dist/helpers/credential-refresh.helpers.d.ts +45 -2
  66. package/dist/helpers/credential-refresh.helpers.d.ts.map +1 -1
  67. package/dist/helpers/credential.helpers.d.ts +4 -2
  68. package/dist/helpers/credential.helpers.d.ts.map +1 -1
  69. package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
  70. package/dist/helpers/inbox-batch-http.helpers.d.ts +15 -0
  71. package/dist/helpers/inbox-batch-http.helpers.d.ts.map +1 -0
  72. package/dist/helpers/inbox-batch-http.helpers.test.d.ts +2 -0
  73. package/dist/helpers/inbox-batch-http.helpers.test.d.ts.map +1 -0
  74. package/dist/helpers/inbox-batch-status.helpers.d.ts +3 -0
  75. package/dist/helpers/inbox-batch-status.helpers.d.ts.map +1 -0
  76. package/dist/helpers/inbox-batch.helpers.d.ts +10 -0
  77. package/dist/helpers/inbox-batch.helpers.d.ts.map +1 -0
  78. package/dist/helpers/inbox-batch.helpers.test.d.ts +2 -0
  79. package/dist/helpers/inbox-batch.helpers.test.d.ts.map +1 -0
  80. package/dist/helpers/inbox-issuance-error.helpers.d.ts +13 -0
  81. package/dist/helpers/inbox-issuance-error.helpers.d.ts.map +1 -0
  82. package/dist/helpers/inbox-queue.helpers.d.ts +11 -0
  83. package/dist/helpers/inbox-queue.helpers.d.ts.map +1 -0
  84. package/dist/helpers/inbox-queue.helpers.test.d.ts +2 -0
  85. package/dist/helpers/inbox-queue.helpers.test.d.ts.map +1 -0
  86. package/dist/helpers/inbox-refresh.helpers.d.ts +186 -0
  87. package/dist/helpers/inbox-refresh.helpers.d.ts.map +1 -0
  88. package/dist/helpers/inbox.helpers.d.ts +9 -1
  89. package/dist/helpers/inbox.helpers.d.ts.map +1 -1
  90. package/dist/helpers/issuedCredentialStatus.helpers.d.ts +5 -0
  91. package/dist/helpers/issuedCredentialStatus.helpers.d.ts.map +1 -0
  92. package/dist/helpers/notifications.helpers.d.ts.map +1 -1
  93. package/dist/helpers/rateLimit.helpers.d.ts.map +1 -1
  94. package/dist/helpers/refresh-send-intent.helpers.d.ts +51 -0
  95. package/dist/helpers/refresh-send-intent.helpers.d.ts.map +1 -0
  96. package/dist/helpers/shim.d.ts +1 -1
  97. package/dist/helpers/shim.d.ts.map +1 -1
  98. package/dist/helpers/signingAuthority.helpers.d.ts +3 -2
  99. package/dist/helpers/signingAuthority.helpers.d.ts.map +1 -1
  100. package/dist/helpers/signingAuthority.helpers.test.d.ts +2 -0
  101. package/dist/helpers/signingAuthority.helpers.test.d.ts.map +1 -0
  102. package/dist/helpers/status-list.helpers.d.ts.map +1 -1
  103. package/dist/helpers/status-list.helpers.test.d.ts +2 -0
  104. package/dist/helpers/status-list.helpers.test.d.ts.map +1 -0
  105. package/dist/inbox-queue-local.d.ts +2 -0
  106. package/dist/inbox-queue-local.d.ts.map +1 -0
  107. package/dist/index.js +2 -2
  108. package/dist/models/AuthGrant.d.ts.map +1 -1
  109. package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
  110. package/dist/models/ConsentFlowTransaction.d.ts.map +1 -1
  111. package/dist/models/Credential.d.ts +1 -0
  112. package/dist/models/Credential.d.ts.map +1 -1
  113. package/dist/models/CredentialActivity.d.ts +1 -0
  114. package/dist/models/CredentialActivity.d.ts.map +1 -1
  115. package/dist/models/CredentialRefresh.d.ts +5 -2
  116. package/dist/models/CredentialRefresh.d.ts.map +1 -1
  117. package/dist/models/InboxCredential.d.ts +1 -0
  118. package/dist/models/InboxCredential.d.ts.map +1 -1
  119. package/dist/models/credential-refresh-constraints.d.ts.map +1 -1
  120. package/dist/openapi.d.ts.map +1 -1
  121. package/dist/routes/act-as.test.d.ts +2 -0
  122. package/dist/routes/act-as.test.d.ts.map +1 -0
  123. package/dist/routes/activity.d.ts +3 -0
  124. package/dist/routes/activity.d.ts.map +1 -1
  125. package/dist/routes/app-store.d.ts.map +1 -1
  126. package/dist/routes/auth-grants.d.ts +10 -0
  127. package/dist/routes/auth-grants.d.ts.map +1 -1
  128. package/dist/routes/boosts.d.ts +506 -12
  129. package/dist/routes/boosts.d.ts.map +1 -1
  130. package/dist/routes/contracts.d.ts +48 -2
  131. package/dist/routes/contracts.d.ts.map +1 -1
  132. package/dist/routes/credential-refreshes.d.ts +4 -4
  133. package/dist/routes/credential-refreshes.d.ts.map +1 -1
  134. package/dist/routes/credentials.d.ts +8 -2
  135. package/dist/routes/credentials.d.ts.map +1 -1
  136. package/dist/routes/did-metadata.d.ts +8 -8
  137. package/dist/routes/federation.d.ts +2 -2
  138. package/dist/routes/inbox.d.ts +973 -31
  139. package/dist/routes/inbox.d.ts.map +1 -1
  140. package/dist/routes/index.d.ts +47 -127
  141. package/dist/routes/index.d.ts.map +1 -1
  142. package/dist/routes/presentations.d.ts +6 -6
  143. package/dist/routes/storage.d.ts +63 -63
  144. package/dist/routes/test.d.ts +6 -0
  145. package/dist/routes/test.d.ts.map +1 -1
  146. package/dist/routes/workflows.d.ts +14 -14
  147. package/dist/routes/workflows.d.ts.map +1 -1
  148. package/dist/services/delivery/adapters/postmark.adapter.d.ts.map +1 -1
  149. package/dist/services/delivery/adapters/postmark.adapter.test.d.ts +2 -0
  150. package/dist/services/delivery/adapters/postmark.adapter.test.d.ts.map +1 -0
  151. package/dist/services/registry/registry.factory.d.ts.map +1 -1
  152. package/dist/types/activity.d.ts +4 -0
  153. package/dist/types/activity.d.ts.map +1 -1
  154. package/dist/types/consentflowcontract.d.ts +22 -8
  155. package/dist/types/consentflowcontract.d.ts.map +1 -1
  156. package/dist/types/credential-refresh.d.ts +10 -1
  157. package/dist/types/credential-refresh.d.ts.map +1 -1
  158. package/dist/types/credential.d.ts +7 -0
  159. package/dist/types/credential.d.ts.map +1 -1
  160. package/dist/types/did-metadata.d.ts +2 -2
  161. package/dist/types/inbox-batch.d.ts +38 -0
  162. package/dist/types/inbox-batch.d.ts.map +1 -0
  163. package/inbox-e2e-setup.ts +53 -1
  164. package/inbox-elasticmq.conf +10 -0
  165. package/lambda.ts +61 -16
  166. package/package.json +18 -16
  167. package/scripts/setup-inbox-queues.ts +60 -0
  168. package/serverless-local.yml +2 -0
  169. package/serverless.yml +75 -0
  170. package/src/accesslayer/app-store-listing/relationships/read.ts +4 -3
  171. package/src/accesslayer/auth-grant/read.ts +2 -1
  172. package/src/accesslayer/consentflowcontract/relationships/create.ts +15 -5
  173. package/src/accesslayer/consentflowcontract/relationships/read.ts +161 -63
  174. package/src/accesslayer/consentflowcontract/relationships/update.ts +43 -10
  175. package/src/accesslayer/credential/create.test.ts +141 -0
  176. package/src/accesslayer/credential/create.ts +27 -3
  177. package/src/accesslayer/credential-activity/create.ts +1 -0
  178. package/src/accesslayer/credential-refresh/email-delivery.ts +116 -0
  179. package/src/accesslayer/credential-refresh/index.ts +1 -0
  180. package/src/accesslayer/credential-refresh/read.ts +8 -2
  181. package/src/accesslayer/credential-refresh/update.ts +4 -0
  182. package/src/accesslayer/inbox-batch/store.ts +469 -0
  183. package/src/accesslayer/inbox-credential/create.ts +51 -19
  184. package/src/accesslayer/inbox-credential/refresh.ts +221 -0
  185. package/src/accesslayer/inbox-credential/update.ts +1 -1
  186. package/src/cache/atomic-counter.test-helpers.ts +27 -0
  187. package/src/cache/atomic-counter.test.ts +6 -0
  188. package/src/cache/index.ts +25 -13
  189. package/src/config/environment.ts +26 -0
  190. package/src/credential-refresh.ts +5 -1
  191. package/src/docker-entry.ts +8 -0
  192. package/src/helpers/activity.helpers.ts +4 -0
  193. package/src/helpers/app-counter.helpers.ts +1 -1
  194. package/src/helpers/boost-storage.helpers.test.ts +242 -0
  195. package/src/helpers/boost.helpers.ts +58 -437
  196. package/src/helpers/contact-method.helpers.test.ts +43 -0
  197. package/src/helpers/contact-method.helpers.ts +3 -4
  198. package/src/helpers/credential-refresh-email-content.helpers.test.ts +222 -0
  199. package/src/helpers/credential-refresh-email-content.helpers.ts +155 -0
  200. package/src/helpers/credential-refresh-email.helpers.ts +201 -0
  201. package/src/helpers/credential-refresh-proof.helpers.test.ts +103 -0
  202. package/src/helpers/credential-refresh-proof.helpers.ts +43 -0
  203. package/src/helpers/credential-refresh.helpers.ts +385 -107
  204. package/src/helpers/credential.helpers.ts +6 -2
  205. package/src/helpers/finalize-inbox.helpers.ts +39 -22
  206. package/src/helpers/inbox-batch-http.helpers.test.ts +188 -0
  207. package/src/helpers/inbox-batch-http.helpers.ts +150 -0
  208. package/src/helpers/inbox-batch-status.helpers.ts +11 -0
  209. package/src/helpers/inbox-batch.helpers.test.ts +668 -0
  210. package/src/helpers/inbox-batch.helpers.ts +374 -0
  211. package/src/helpers/inbox-issuance-error.helpers.ts +25 -0
  212. package/src/helpers/inbox-queue.helpers.test.ts +253 -0
  213. package/src/helpers/inbox-queue.helpers.ts +372 -0
  214. package/src/helpers/inbox-refresh.helpers.ts +432 -0
  215. package/src/helpers/inbox.helpers.ts +301 -50
  216. package/src/helpers/issuedCredentialStatus.helpers.ts +11 -0
  217. package/src/helpers/notifications.helpers.ts +4 -2
  218. package/src/helpers/rateLimit.helpers.ts +2 -5
  219. package/src/helpers/refresh-send-intent.helpers.ts +273 -0
  220. package/src/helpers/shim.ts +32 -31
  221. package/src/helpers/signingAuthority.helpers.test.ts +198 -0
  222. package/src/helpers/signingAuthority.helpers.ts +41 -18
  223. package/src/helpers/status-list.helpers.test.ts +77 -0
  224. package/src/helpers/status-list.helpers.ts +31 -8
  225. package/src/inbox-queue-local.ts +28 -0
  226. package/src/models/AuthGrant.ts +1 -0
  227. package/src/models/ConsentFlowTerms.ts +4 -0
  228. package/src/models/ConsentFlowTransaction.ts +4 -0
  229. package/src/models/Credential.ts +1 -0
  230. package/src/models/CredentialActivity.ts +3 -5
  231. package/src/models/CredentialRefresh.ts +4 -2
  232. package/src/models/InboxCredential.ts +2 -0
  233. package/src/models/credential-refresh-constraints.test.ts +8 -3
  234. package/src/models/credential-refresh-constraints.ts +5 -0
  235. package/src/openapi.ts +8 -0
  236. package/src/routes/act-as.test.ts +239 -0
  237. package/src/routes/app-store.ts +69 -52
  238. package/src/routes/auth-grants.ts +13 -11
  239. package/src/routes/boosts.ts +943 -120
  240. package/src/routes/contracts.ts +55 -7
  241. package/src/routes/credential-refreshes.test.ts +8 -2
  242. package/src/routes/credential-refreshes.ts +4 -1
  243. package/src/routes/credentials.ts +2 -1
  244. package/src/routes/inbox.ts +95 -49
  245. package/src/routes/index.ts +164 -88
  246. package/src/routes/skills.ts +1 -1
  247. package/src/routes/workflows.ts +80 -31
  248. package/src/services/delivery/adapters/postmark.adapter.test.ts +54 -0
  249. package/src/services/delivery/adapters/postmark.adapter.ts +12 -0
  250. package/src/services/registry/registry.factory.ts +2 -1
  251. package/src/types/activity.ts +2 -0
  252. package/src/types/consentflowcontract.ts +21 -3
  253. package/src/types/credential-refresh.ts +21 -2
  254. package/src/types/credential.ts +13 -1
  255. package/src/types/inbox-batch.ts +46 -0
  256. package/test/app-store-listing.spec.ts +66 -30
  257. package/test/atomic-counter.e2e.ts +12 -0
  258. package/test/auth-grant.spec.ts +21 -21
  259. package/test/bitstring-status-list.spec.ts +12 -9
  260. package/test/consentflow.spec.ts +453 -26
  261. package/test/credential-refresh-email.spec.ts +663 -0
  262. package/test/credential-refresh-publication.spec.ts +13 -5
  263. package/test/guardian-gated-routes.spec.ts +337 -180
  264. package/test/helpers/inbox-e2e-environment.ts +6 -0
  265. package/test/inbox-batch.spec.ts +1338 -0
  266. package/test/inbox-refresh.spec.ts +534 -0
  267. package/test/inbox-security.e2e.ts +48 -3
  268. package/test/openapi.spec.ts +41 -1
  269. package/test/refresh-send.spec.ts +1799 -0
  270. package/test/registry.spec.ts +56 -11
  271. package/test/revoke-suspend-per-instance.spec.ts +9 -1
  272. package/vitest.config.ts +13 -0
  273. package/vitest.inbox-e2e.config.ts +5 -1
  274. package/vitest.integration.config.ts +2 -0
@@ -6,7 +6,7 @@ import {
6
6
  LCNNotificationTypeEnumValidator,
7
7
  SendNotificationEventValidator,
8
8
  } from '@learncard/types';
9
- import type { JWE, UnsignedVC, VC } from '@learncard/types';
9
+ import type { UnsignedVC } from '@learncard/types';
10
10
  import { isVC2Format, checkAppInstallEligibility, calculateAgeFromDob } from '@learncard/helpers';
11
11
  import type { ProfileType } from 'types/profile';
12
12
 
@@ -107,6 +107,7 @@ import {
107
107
  import { createBoostForListing } from '@accesslayer/boost/create';
108
108
  import { setBoostAsParent } from '@accesslayer/boost/relationships/create';
109
109
  import { issueCredentialWithSigningAuthority } from '@helpers/signingAuthority.helpers';
110
+ import type { IssuedCredential } from '../types/credential';
110
111
  import {
111
112
  renderBoostTemplate,
112
113
  parseRenderedTemplate,
@@ -123,7 +124,7 @@ import {
123
124
  getCredentialInstanceForBoostAndProfile,
124
125
  } from '@accesslayer/credential/read';
125
126
  import {
126
- getContractTermsForProfile,
127
+ getConsentedDataBetweenProfiles,
127
128
  getContractDetailsByUri,
128
129
  } from '@accesslayer/consentflowcontract/relationships/read';
129
130
  import { getBoostPermissions, getBoostRecipients } from '@accesslayer/boost/relationships/read';
@@ -724,7 +725,7 @@ export const handleSendCredentialEvent = async (
724
725
  };
725
726
 
726
727
  // Issue via signing authority
727
- let credential: VC | JWE;
728
+ let credential: IssuedCredential;
728
729
 
729
730
  try {
730
731
  const ownerDidOverride = listing.slug ? getAppDidWeb(ctx.domain, listing.slug) : undefined;
@@ -1122,8 +1123,8 @@ const handleGetTemplateRecipientsEvent = async (
1122
1123
  sent.status === 'suspended'
1123
1124
  ? ('suspended' as const)
1124
1125
  : received
1125
- ? ('claimed' as const)
1126
- : ('pending' as const),
1126
+ ? ('claimed' as const)
1127
+ : ('pending' as const),
1127
1128
  };
1128
1129
  })
1129
1130
  .filter(
@@ -1159,10 +1160,10 @@ const handleGetTemplateRecipientsEvent = async (
1159
1160
  record.status === 'revoked'
1160
1161
  ? ('revoked' as const)
1161
1162
  : record.status === 'suspended'
1162
- ? ('suspended' as const)
1163
- : record.received
1164
- ? ('claimed' as const)
1165
- : ('pending' as const),
1163
+ ? ('suspended' as const)
1164
+ : record.received
1165
+ ? ('claimed' as const)
1166
+ : ('pending' as const),
1166
1167
  })),
1167
1168
  ]
1168
1169
  .filter(record => Boolean(record.credentialUri))
@@ -1198,59 +1199,75 @@ const handleRequestLearnerContextEvent = async (
1198
1199
  detailLevel?: string;
1199
1200
  };
1200
1201
 
1201
- const credentialUris: string[] = [];
1202
+ const credentialUris = new Set<string>();
1202
1203
  let personalData: Record<string, string> = {};
1203
-
1204
- const sentCredentials = await getCredentialsSentByListingToProfile(
1205
- listingId,
1206
- profile.profileId,
1207
- { limit: 100 }
1208
- );
1209
-
1210
- for (const sentCred of sentCredentials) {
1211
- const credentialUri = getCredentialUri(sentCred.credentialId, ctx.domain);
1212
- credentialUris.push(credentialUri);
1213
- }
1214
-
1215
- // Resolve contractUri from launch_config_json or guideState
1216
- let contractUri = getContractUriFromLaunchConfig(listing);
1217
-
1218
- if (!contractUri) {
1219
- const integration = await getIntegrationForListing(listingId);
1220
- contractUri = getContractUriFromGuideState(integration);
1221
- }
1204
+ const integration = await getIntegrationForListing(listingId);
1205
+ const contractUri =
1206
+ getContractUriFromLaunchConfig(listing) ?? getContractUriFromGuideState(integration);
1222
1207
 
1223
1208
  if (contractUri) {
1224
- try {
1225
- const contractDetails = await getContractDetailsByUri(contractUri);
1226
-
1227
- if (contractDetails?.contract) {
1228
- const terms = await getContractTermsForProfile(profile, contractDetails.contract);
1229
-
1230
- if (event.includeCredentials && terms?.terms?.read?.credentials) {
1231
- const uris = Object.values(terms.terms.read.credentials.categories).flatMap(
1232
- (category: unknown) => (category as { shared?: string[] })?.shared ?? []
1233
- );
1234
-
1235
- credentialUris.push(...uris);
1236
- }
1209
+ const contractDetails = await getContractDetailsByUri(contractUri);
1210
+ const owner = integration ? await getOwnerProfileForIntegration(integration.id) : null;
1211
+ if (
1212
+ !contractDetails ||
1213
+ !owner ||
1214
+ contractDetails.contractOwner.profileId !== owner.profileId
1215
+ ) {
1216
+ throw new TRPCError({
1217
+ code: 'FORBIDDEN',
1218
+ message: 'The app must use a consent contract owned by its integration',
1219
+ });
1220
+ }
1237
1221
 
1238
- if (event.includePersonalData && terms?.terms?.read?.personal) {
1239
- personalData = terms.terms.read.personal;
1240
- }
1222
+ // Use the same live/expiry/category/guardian evaluation as data-provider reads.
1223
+ // An app's contract is not interchangeable with another app's or AI's grant.
1224
+ const [consent] = await getConsentedDataBetweenProfiles(
1225
+ owner.profileId,
1226
+ profile.profileId,
1227
+ { query: { id: contractDetails.contract.id }, limit: 1, domain: ctx.domain }
1228
+ );
1229
+ if (
1230
+ !consent ||
1231
+ consent.status !== 'live' ||
1232
+ (consent.guardian.required && !consent.guardian.approved)
1233
+ ) {
1234
+ throw new TRPCError({
1235
+ code: 'FORBIDDEN',
1236
+ message: 'Current app consent and applicable guardian approval are required',
1237
+ });
1238
+ }
1239
+ if (event.includeCredentials === true) {
1240
+ for (const credential of consent.credentials) credentialUris.add(credential.uri);
1241
+ }
1242
+ if (event.includePersonalData === true) personalData = consent.personal;
1243
+ } else if (event.includeCredentials === true) {
1244
+ // Without a read contract, only this app's own accepted, active issuances
1245
+ // are available. Never supplement a restricted contract with these records.
1246
+ const sentCredentials = await getCredentialsSentByListingToProfile(
1247
+ listingId,
1248
+ profile.profileId,
1249
+ { limit: 501 }
1250
+ );
1251
+ if (sentCredentials.length > 500) {
1252
+ throw new TRPCError({
1253
+ code: 'BAD_REQUEST',
1254
+ message: 'Learner context supports up to 500 selected credentials',
1255
+ });
1256
+ }
1257
+ for (const credential of sentCredentials) {
1258
+ if (credential.status === 'claimed') {
1259
+ credentialUris.add(getCredentialUri(credential.credentialId, ctx.domain));
1241
1260
  }
1242
- } catch (error) {
1243
- console.error('Error fetching contract credentials:', error);
1244
1261
  }
1245
1262
  }
1246
1263
 
1247
1264
  return {
1248
- credentialUris,
1265
+ credentialUris: [...credentialUris],
1249
1266
  personalData,
1250
1267
  instructions,
1251
1268
  detailLevel,
1252
- maxCredentials: credentialUris.length,
1253
- did: `did:web:${ctx.domain}:${profile.profileId}`,
1269
+ maxCredentials: credentialUris.size,
1270
+ did: getDidWeb(ctx.domain, profile.profileId),
1254
1271
  };
1255
1272
  };
1256
1273
 
@@ -1654,7 +1671,7 @@ const handleSendNotificationEvent = async (
1654
1671
  // tRPC does not support HTTP 429 natively, so we cast to BAD_REQUEST
1655
1672
  // while keeping the semantic code in the message for clients.
1656
1673
  throw new TRPCError({
1657
- code: 'TOO_MANY_REQUESTS' as 'BAD_REQUEST',
1674
+ code: 'TOO_MANY_REQUESTS',
1658
1675
  message: 'Rate limit exceeded: max 10 notifications per user per app per hour',
1659
1676
  });
1660
1677
  }
@@ -2701,7 +2718,7 @@ export const appStoreRouter = t.router({
2701
2718
  // tRPC does not support HTTP 429 natively, so we cast to BAD_REQUEST
2702
2719
  // while keeping the semantic code in the message for clients.
2703
2720
  throw new TRPCError({
2704
- code: 'TOO_MANY_REQUESTS' as 'BAD_REQUEST',
2721
+ code: 'TOO_MANY_REQUESTS',
2705
2722
  message: 'Rate limit exceeded: max 60 notifications per app per hour',
2706
2723
  });
2707
2724
  }
@@ -139,14 +139,9 @@ export const authGrantsRouter = t.router({
139
139
  .input(
140
140
  z.object({
141
141
  id: z.string(),
142
- updates: AuthGrantValidator.partial().omit({
143
- id: true,
144
- scope: true,
145
- status: true,
146
- createdAt: true,
147
- expiresAt: true,
148
- challenge: true,
149
- }),
142
+ // Immutable fields stay in the schema so the guard below can reject them
143
+ // explicitly; omitting them would strip the key and silently no-op instead.
144
+ updates: AuthGrantValidator.partial().strict(),
150
145
  })
151
146
  )
152
147
  .output(z.boolean())
@@ -158,13 +153,20 @@ export const authGrantsRouter = t.router({
158
153
  });
159
154
  }
160
155
 
161
- // Extra check to reject sensitive, invalid updates
162
- const invalidUpdates = ['id', 'scope', 'status', 'createdAt', 'expiresAt', 'challenge'];
156
+ const invalidUpdates = [
157
+ 'id',
158
+ 'scope',
159
+ 'actAs',
160
+ 'status',
161
+ 'createdAt',
162
+ 'expiresAt',
163
+ 'challenge',
164
+ ];
163
165
  if (invalidUpdates.some(key => input.updates.hasOwnProperty(key))) {
164
166
  throw new TRPCError({
165
167
  code: 'BAD_REQUEST',
166
168
  message:
167
- 'Cannot update id, scope, status, createdAt, expiresAt, or challenge of an AuthGrant',
169
+ 'Cannot update id, scope, actAs, status, createdAt, expiresAt, or challenge of an AuthGrant',
168
170
  });
169
171
  }
170
172