@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.
- package/.env.example +4 -0
- package/AGENTS.md +177 -159
- package/CHANGELOG.md +148 -0
- package/README.md +26 -8
- package/compose.inbox.yml +7 -0
- package/dist/accesslayer/app-store-listing/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/auth-grant/read.d.ts +1 -0
- package/dist/accesslayer/auth-grant/read.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +3 -2
- package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +1 -0
- package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +5 -3
- package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
- package/dist/accesslayer/credential/create.d.ts +2 -1
- package/dist/accesslayer/credential/create.d.ts.map +1 -1
- package/dist/accesslayer/credential/create.test.d.ts +2 -0
- package/dist/accesslayer/credential/create.test.d.ts.map +1 -0
- package/dist/accesslayer/credential/relationships/read.d.ts +1 -0
- package/dist/accesslayer/credential/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/credential-activity/create.d.ts.map +1 -1
- package/dist/accesslayer/credential-refresh/email-delivery.d.ts +18 -0
- package/dist/accesslayer/credential-refresh/email-delivery.d.ts.map +1 -0
- package/dist/accesslayer/credential-refresh/index.d.ts +1 -0
- package/dist/accesslayer/credential-refresh/index.d.ts.map +1 -1
- package/dist/accesslayer/credential-refresh/read.d.ts.map +1 -1
- package/dist/accesslayer/credential-refresh/update.d.ts.map +1 -1
- package/dist/accesslayer/inbox-batch/store.d.ts +33 -0
- package/dist/accesslayer/inbox-batch/store.d.ts.map +1 -0
- package/dist/accesslayer/inbox-credential/create.d.ts +5 -0
- package/dist/accesslayer/inbox-credential/create.d.ts.map +1 -1
- package/dist/accesslayer/inbox-credential/refresh.d.ts +71 -0
- package/dist/accesslayer/inbox-credential/refresh.d.ts.map +1 -0
- package/dist/accesslayer/inbox-credential/update.d.ts +1 -1
- package/dist/accesslayer/inbox-credential/update.d.ts.map +1 -1
- package/dist/cache/atomic-counter.test-helpers.d.ts +3 -0
- package/dist/cache/atomic-counter.test-helpers.d.ts.map +1 -0
- package/dist/cache/atomic-counter.test.d.ts +2 -0
- package/dist/cache/atomic-counter.test.d.ts.map +1 -0
- package/dist/cache/index.d.ts +1 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/config/environment.d.ts +46 -0
- package/dist/config/environment.d.ts.map +1 -1
- package/dist/credential-refresh.d.ts.map +1 -1
- package/dist/didkit_wasm_bg.wasm +0 -0
- package/dist/helpers/activity.helpers.d.ts +4 -0
- package/dist/helpers/activity.helpers.d.ts.map +1 -1
- package/dist/helpers/boost-storage.helpers.test.d.ts +2 -0
- package/dist/helpers/boost-storage.helpers.test.d.ts.map +1 -0
- package/dist/helpers/boost.helpers.d.ts +3 -7
- package/dist/helpers/boost.helpers.d.ts.map +1 -1
- package/dist/helpers/contact-method.helpers.d.ts.map +1 -1
- package/dist/helpers/contact-method.helpers.test.d.ts +2 -0
- package/dist/helpers/contact-method.helpers.test.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-email-content.helpers.d.ts +44 -0
- package/dist/helpers/credential-refresh-email-content.helpers.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-email-content.helpers.test.d.ts +2 -0
- package/dist/helpers/credential-refresh-email-content.helpers.test.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-email.helpers.d.ts +17 -0
- package/dist/helpers/credential-refresh-email.helpers.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-proof.helpers.d.ts +4 -0
- package/dist/helpers/credential-refresh-proof.helpers.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-proof.helpers.test.d.ts +2 -0
- package/dist/helpers/credential-refresh-proof.helpers.test.d.ts.map +1 -0
- package/dist/helpers/credential-refresh.helpers.d.ts +45 -2
- package/dist/helpers/credential-refresh.helpers.d.ts.map +1 -1
- package/dist/helpers/credential.helpers.d.ts +4 -2
- package/dist/helpers/credential.helpers.d.ts.map +1 -1
- package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
- package/dist/helpers/inbox-batch-http.helpers.d.ts +15 -0
- package/dist/helpers/inbox-batch-http.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-batch-http.helpers.test.d.ts +2 -0
- package/dist/helpers/inbox-batch-http.helpers.test.d.ts.map +1 -0
- package/dist/helpers/inbox-batch-status.helpers.d.ts +3 -0
- package/dist/helpers/inbox-batch-status.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-batch.helpers.d.ts +10 -0
- package/dist/helpers/inbox-batch.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-batch.helpers.test.d.ts +2 -0
- package/dist/helpers/inbox-batch.helpers.test.d.ts.map +1 -0
- package/dist/helpers/inbox-issuance-error.helpers.d.ts +13 -0
- package/dist/helpers/inbox-issuance-error.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-queue.helpers.d.ts +11 -0
- package/dist/helpers/inbox-queue.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-queue.helpers.test.d.ts +2 -0
- package/dist/helpers/inbox-queue.helpers.test.d.ts.map +1 -0
- package/dist/helpers/inbox-refresh.helpers.d.ts +186 -0
- package/dist/helpers/inbox-refresh.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox.helpers.d.ts +9 -1
- package/dist/helpers/inbox.helpers.d.ts.map +1 -1
- package/dist/helpers/issuedCredentialStatus.helpers.d.ts +5 -0
- package/dist/helpers/issuedCredentialStatus.helpers.d.ts.map +1 -0
- package/dist/helpers/notifications.helpers.d.ts.map +1 -1
- package/dist/helpers/rateLimit.helpers.d.ts.map +1 -1
- package/dist/helpers/refresh-send-intent.helpers.d.ts +51 -0
- package/dist/helpers/refresh-send-intent.helpers.d.ts.map +1 -0
- package/dist/helpers/shim.d.ts +1 -1
- package/dist/helpers/shim.d.ts.map +1 -1
- package/dist/helpers/signingAuthority.helpers.d.ts +3 -2
- package/dist/helpers/signingAuthority.helpers.d.ts.map +1 -1
- package/dist/helpers/signingAuthority.helpers.test.d.ts +2 -0
- package/dist/helpers/signingAuthority.helpers.test.d.ts.map +1 -0
- package/dist/helpers/status-list.helpers.d.ts.map +1 -1
- package/dist/helpers/status-list.helpers.test.d.ts +2 -0
- package/dist/helpers/status-list.helpers.test.d.ts.map +1 -0
- package/dist/inbox-queue-local.d.ts +2 -0
- package/dist/inbox-queue-local.d.ts.map +1 -0
- package/dist/index.js +2 -2
- package/dist/models/AuthGrant.d.ts.map +1 -1
- package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
- package/dist/models/ConsentFlowTransaction.d.ts.map +1 -1
- package/dist/models/Credential.d.ts +1 -0
- package/dist/models/Credential.d.ts.map +1 -1
- package/dist/models/CredentialActivity.d.ts +1 -0
- package/dist/models/CredentialActivity.d.ts.map +1 -1
- package/dist/models/CredentialRefresh.d.ts +5 -2
- package/dist/models/CredentialRefresh.d.ts.map +1 -1
- package/dist/models/InboxCredential.d.ts +1 -0
- package/dist/models/InboxCredential.d.ts.map +1 -1
- package/dist/models/credential-refresh-constraints.d.ts.map +1 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/routes/act-as.test.d.ts +2 -0
- package/dist/routes/act-as.test.d.ts.map +1 -0
- package/dist/routes/activity.d.ts +3 -0
- package/dist/routes/activity.d.ts.map +1 -1
- package/dist/routes/app-store.d.ts.map +1 -1
- package/dist/routes/auth-grants.d.ts +10 -0
- package/dist/routes/auth-grants.d.ts.map +1 -1
- package/dist/routes/boosts.d.ts +506 -12
- package/dist/routes/boosts.d.ts.map +1 -1
- package/dist/routes/contracts.d.ts +48 -2
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/credential-refreshes.d.ts +4 -4
- package/dist/routes/credential-refreshes.d.ts.map +1 -1
- package/dist/routes/credentials.d.ts +8 -2
- package/dist/routes/credentials.d.ts.map +1 -1
- package/dist/routes/did-metadata.d.ts +8 -8
- package/dist/routes/federation.d.ts +2 -2
- package/dist/routes/inbox.d.ts +973 -31
- package/dist/routes/inbox.d.ts.map +1 -1
- package/dist/routes/index.d.ts +47 -127
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/presentations.d.ts +6 -6
- package/dist/routes/storage.d.ts +63 -63
- package/dist/routes/test.d.ts +6 -0
- package/dist/routes/test.d.ts.map +1 -1
- package/dist/routes/workflows.d.ts +14 -14
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/services/delivery/adapters/postmark.adapter.d.ts.map +1 -1
- package/dist/services/delivery/adapters/postmark.adapter.test.d.ts +2 -0
- package/dist/services/delivery/adapters/postmark.adapter.test.d.ts.map +1 -0
- package/dist/services/registry/registry.factory.d.ts.map +1 -1
- package/dist/types/activity.d.ts +4 -0
- package/dist/types/activity.d.ts.map +1 -1
- package/dist/types/consentflowcontract.d.ts +22 -8
- package/dist/types/consentflowcontract.d.ts.map +1 -1
- package/dist/types/credential-refresh.d.ts +10 -1
- package/dist/types/credential-refresh.d.ts.map +1 -1
- package/dist/types/credential.d.ts +7 -0
- package/dist/types/credential.d.ts.map +1 -1
- package/dist/types/did-metadata.d.ts +2 -2
- package/dist/types/inbox-batch.d.ts +38 -0
- package/dist/types/inbox-batch.d.ts.map +1 -0
- package/inbox-e2e-setup.ts +53 -1
- package/inbox-elasticmq.conf +10 -0
- package/lambda.ts +61 -16
- package/package.json +18 -16
- package/scripts/setup-inbox-queues.ts +60 -0
- package/serverless-local.yml +2 -0
- package/serverless.yml +75 -0
- package/src/accesslayer/app-store-listing/relationships/read.ts +4 -3
- package/src/accesslayer/auth-grant/read.ts +2 -1
- package/src/accesslayer/consentflowcontract/relationships/create.ts +15 -5
- package/src/accesslayer/consentflowcontract/relationships/read.ts +161 -63
- package/src/accesslayer/consentflowcontract/relationships/update.ts +43 -10
- package/src/accesslayer/credential/create.test.ts +141 -0
- package/src/accesslayer/credential/create.ts +27 -3
- package/src/accesslayer/credential-activity/create.ts +1 -0
- package/src/accesslayer/credential-refresh/email-delivery.ts +116 -0
- package/src/accesslayer/credential-refresh/index.ts +1 -0
- package/src/accesslayer/credential-refresh/read.ts +8 -2
- package/src/accesslayer/credential-refresh/update.ts +4 -0
- package/src/accesslayer/inbox-batch/store.ts +469 -0
- package/src/accesslayer/inbox-credential/create.ts +51 -19
- package/src/accesslayer/inbox-credential/refresh.ts +221 -0
- package/src/accesslayer/inbox-credential/update.ts +1 -1
- package/src/cache/atomic-counter.test-helpers.ts +27 -0
- package/src/cache/atomic-counter.test.ts +6 -0
- package/src/cache/index.ts +25 -13
- package/src/config/environment.ts +26 -0
- package/src/credential-refresh.ts +5 -1
- package/src/docker-entry.ts +8 -0
- package/src/helpers/activity.helpers.ts +4 -0
- package/src/helpers/app-counter.helpers.ts +1 -1
- package/src/helpers/boost-storage.helpers.test.ts +242 -0
- package/src/helpers/boost.helpers.ts +58 -437
- package/src/helpers/contact-method.helpers.test.ts +43 -0
- package/src/helpers/contact-method.helpers.ts +3 -4
- package/src/helpers/credential-refresh-email-content.helpers.test.ts +222 -0
- package/src/helpers/credential-refresh-email-content.helpers.ts +155 -0
- package/src/helpers/credential-refresh-email.helpers.ts +201 -0
- package/src/helpers/credential-refresh-proof.helpers.test.ts +103 -0
- package/src/helpers/credential-refresh-proof.helpers.ts +43 -0
- package/src/helpers/credential-refresh.helpers.ts +385 -107
- package/src/helpers/credential.helpers.ts +6 -2
- package/src/helpers/finalize-inbox.helpers.ts +39 -22
- package/src/helpers/inbox-batch-http.helpers.test.ts +188 -0
- package/src/helpers/inbox-batch-http.helpers.ts +150 -0
- package/src/helpers/inbox-batch-status.helpers.ts +11 -0
- package/src/helpers/inbox-batch.helpers.test.ts +668 -0
- package/src/helpers/inbox-batch.helpers.ts +374 -0
- package/src/helpers/inbox-issuance-error.helpers.ts +25 -0
- package/src/helpers/inbox-queue.helpers.test.ts +253 -0
- package/src/helpers/inbox-queue.helpers.ts +372 -0
- package/src/helpers/inbox-refresh.helpers.ts +432 -0
- package/src/helpers/inbox.helpers.ts +301 -50
- package/src/helpers/issuedCredentialStatus.helpers.ts +11 -0
- package/src/helpers/notifications.helpers.ts +4 -2
- package/src/helpers/rateLimit.helpers.ts +2 -5
- package/src/helpers/refresh-send-intent.helpers.ts +273 -0
- package/src/helpers/shim.ts +32 -31
- package/src/helpers/signingAuthority.helpers.test.ts +198 -0
- package/src/helpers/signingAuthority.helpers.ts +41 -18
- package/src/helpers/status-list.helpers.test.ts +77 -0
- package/src/helpers/status-list.helpers.ts +31 -8
- package/src/inbox-queue-local.ts +28 -0
- package/src/models/AuthGrant.ts +1 -0
- package/src/models/ConsentFlowTerms.ts +4 -0
- package/src/models/ConsentFlowTransaction.ts +4 -0
- package/src/models/Credential.ts +1 -0
- package/src/models/CredentialActivity.ts +3 -5
- package/src/models/CredentialRefresh.ts +4 -2
- package/src/models/InboxCredential.ts +2 -0
- package/src/models/credential-refresh-constraints.test.ts +8 -3
- package/src/models/credential-refresh-constraints.ts +5 -0
- package/src/openapi.ts +8 -0
- package/src/routes/act-as.test.ts +239 -0
- package/src/routes/app-store.ts +69 -52
- package/src/routes/auth-grants.ts +13 -11
- package/src/routes/boosts.ts +943 -120
- package/src/routes/contracts.ts +55 -7
- package/src/routes/credential-refreshes.test.ts +8 -2
- package/src/routes/credential-refreshes.ts +4 -1
- package/src/routes/credentials.ts +2 -1
- package/src/routes/inbox.ts +95 -49
- package/src/routes/index.ts +164 -88
- package/src/routes/skills.ts +1 -1
- package/src/routes/workflows.ts +80 -31
- package/src/services/delivery/adapters/postmark.adapter.test.ts +54 -0
- package/src/services/delivery/adapters/postmark.adapter.ts +12 -0
- package/src/services/registry/registry.factory.ts +2 -1
- package/src/types/activity.ts +2 -0
- package/src/types/consentflowcontract.ts +21 -3
- package/src/types/credential-refresh.ts +21 -2
- package/src/types/credential.ts +13 -1
- package/src/types/inbox-batch.ts +46 -0
- package/test/app-store-listing.spec.ts +66 -30
- package/test/atomic-counter.e2e.ts +12 -0
- package/test/auth-grant.spec.ts +21 -21
- package/test/bitstring-status-list.spec.ts +12 -9
- package/test/consentflow.spec.ts +453 -26
- package/test/credential-refresh-email.spec.ts +663 -0
- package/test/credential-refresh-publication.spec.ts +13 -5
- package/test/guardian-gated-routes.spec.ts +337 -180
- package/test/helpers/inbox-e2e-environment.ts +6 -0
- package/test/inbox-batch.spec.ts +1338 -0
- package/test/inbox-refresh.spec.ts +534 -0
- package/test/inbox-security.e2e.ts +48 -3
- package/test/openapi.spec.ts +41 -1
- package/test/refresh-send.spec.ts +1799 -0
- package/test/registry.spec.ts +56 -11
- package/test/revoke-suspend-per-instance.spec.ts +9 -1
- package/vitest.config.ts +13 -0
- package/vitest.inbox-e2e.config.ts +5 -1
- package/vitest.integration.config.ts +2 -0
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { environment } from '@environment';
|
|
2
|
-
import isEqual from 'lodash/isEqual';
|
|
3
1
|
import cloneDeep from 'lodash/cloneDeep';
|
|
4
|
-
import {
|
|
2
|
+
import { isEncrypted, isVC2Format } from '@learncard/helpers';
|
|
5
3
|
import { TRPCError } from '@trpc/server';
|
|
6
4
|
import {
|
|
7
5
|
VC,
|
|
@@ -10,7 +8,6 @@ import {
|
|
|
10
8
|
LCNNotificationTypeEnumValidator,
|
|
11
9
|
ContactMethodQueryType,
|
|
12
10
|
} from '@learncard/types';
|
|
13
|
-
import { isEncrypted, isVC2Format } from '@learncard/helpers';
|
|
14
11
|
import { ProfileType, SigningAuthorityForUserType } from 'types/profile';
|
|
15
12
|
import {
|
|
16
13
|
CredentialIssuer,
|
|
@@ -18,13 +15,9 @@ import {
|
|
|
18
15
|
getIssuerOwnerProfile,
|
|
19
16
|
getIssuerProfileId,
|
|
20
17
|
} from '../types/issuer';
|
|
21
|
-
import { trace, traceDb
|
|
18
|
+
import { trace, traceDb } from '@tracing';
|
|
19
|
+
import { injectObv3AlignmentsIntoCredentialForBoost } from '@services/skills-provider/inject';
|
|
22
20
|
import {
|
|
23
|
-
injectObv3AlignmentsIntoCredentialForBoost,
|
|
24
|
-
buildObv3AlignmentsForBoost,
|
|
25
|
-
} from '@services/skills-provider/inject';
|
|
26
|
-
import {
|
|
27
|
-
hasMustacheVariables,
|
|
28
21
|
renderBoostTemplate,
|
|
29
22
|
parseRenderedTemplate,
|
|
30
23
|
shouldAutoAppendTemplateEvidence,
|
|
@@ -41,7 +34,6 @@ import {
|
|
|
41
34
|
createListingSentCredentialRelationship,
|
|
42
35
|
} from '@accesslayer/credential/relationships/create';
|
|
43
36
|
import { acceptCredential, getCredentialUri } from './credential.helpers';
|
|
44
|
-
import { getLearnCard } from './learnCard.helpers';
|
|
45
37
|
import { issueCredentialWithSigningAuthority } from './signingAuthority.helpers';
|
|
46
38
|
import { addNotificationToQueue } from './notifications.helpers';
|
|
47
39
|
import { getNotificationMessage } from './notificationMessages';
|
|
@@ -50,6 +42,8 @@ import { BoostStatus, getBoostOwnerProfile } from 'types/boost';
|
|
|
50
42
|
import { getDidWeb } from './did.helpers';
|
|
51
43
|
import { DbTermsType } from 'types/consentflowcontract';
|
|
52
44
|
import { appendBitstringStatusListEntries } from './status-list.helpers';
|
|
45
|
+
import { getIssuedCredentialPayload } from './issuedCredentialStatus.helpers';
|
|
46
|
+
import type { IssuedCredential } from '../types/credential';
|
|
53
47
|
|
|
54
48
|
export const getBoostUri = (id: string, domain: string): string =>
|
|
55
49
|
constructUri('boost', id, domain);
|
|
@@ -113,274 +107,6 @@ export const convertCredentialToBoostTemplateJSON = (
|
|
|
113
107
|
return JSON.stringify(template);
|
|
114
108
|
};
|
|
115
109
|
|
|
116
|
-
export const verifyCredentialIsDerivedFromBoost = async (
|
|
117
|
-
boost: BoostInstance,
|
|
118
|
-
credential: VC,
|
|
119
|
-
domain: string
|
|
120
|
-
): Promise<boolean> => {
|
|
121
|
-
if (!boost || !credential) {
|
|
122
|
-
console.error('Either boost or credential do not exist.', credential, credential);
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const boostCredential = JSON.parse(boost?.dataValues?.boost);
|
|
127
|
-
const boostId = boost?.dataValues?.id;
|
|
128
|
-
const boostURI = getBoostUri(boostId, domain);
|
|
129
|
-
|
|
130
|
-
if (!boostCredential) {
|
|
131
|
-
console.error('No credential template attached to boost');
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (!isEqual(credential.boostId, boostURI)) {
|
|
136
|
-
console.error('Credential boostId !== boost id', credential.boostId, boostURI);
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// If the boost template contains Mustache variables, we use a more lenient verification.
|
|
141
|
-
// We only verify that the credential type matches the template type, since other fields
|
|
142
|
-
// may have been dynamically substituted at issuance time.
|
|
143
|
-
const boostTemplateString = boost?.dataValues?.boost;
|
|
144
|
-
const isTemplatedBoost = hasMustacheVariables(boostTemplateString);
|
|
145
|
-
|
|
146
|
-
if (isTemplatedBoost) {
|
|
147
|
-
// For templated boosts, only verify the type matches
|
|
148
|
-
if (!isEqual(credential.type, boostCredential.type)) {
|
|
149
|
-
console.error(
|
|
150
|
-
'Credential type !== boost credential type (templated boost)',
|
|
151
|
-
credential.type,
|
|
152
|
-
boostCredential.type
|
|
153
|
-
);
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
// Templated boost passed basic verification
|
|
157
|
-
return true;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/// Simplify Comparison
|
|
161
|
-
// if (
|
|
162
|
-
// !isEqual(credential.boostId, boostId) ||
|
|
163
|
-
// !isEqual(credential.type, boostCredential.type) ||
|
|
164
|
-
// !isEqual(
|
|
165
|
-
// credential.credentialSubject?.achievement,
|
|
166
|
-
// boostCredential.credentialSubject?.achievement
|
|
167
|
-
// ) ||
|
|
168
|
-
// !isEqual(credential.display, boostCredential.display) ||
|
|
169
|
-
// isEqual(credential.image, boostCredential.image) ||
|
|
170
|
-
// !isEqual(credential.attachments, boostCredential.attachments)
|
|
171
|
-
// ) {
|
|
172
|
-
// return false;
|
|
173
|
-
// }
|
|
174
|
-
|
|
175
|
-
if (!isEqual(credential.type, boostCredential.type)) {
|
|
176
|
-
console.error(
|
|
177
|
-
'Credential type !== boost credential type',
|
|
178
|
-
credential.type,
|
|
179
|
-
boostCredential.type
|
|
180
|
-
);
|
|
181
|
-
return false;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Mirror plugin behavior: inject OBv3 alignments into the expected template before comparison.
|
|
185
|
-
// Additionally, warn (but allow) if the credential would match the base template without alignments.
|
|
186
|
-
if (
|
|
187
|
-
credential.credentialSubject &&
|
|
188
|
-
'achievement' in credential.credentialSubject &&
|
|
189
|
-
typeof (credential as any).credentialSubject.achievement === 'object'
|
|
190
|
-
) {
|
|
191
|
-
try {
|
|
192
|
-
// Build expected boost credential with OBv3 alignments injected (JSON-LD typed as in plugin)
|
|
193
|
-
const baseExpected = boostCredential as VC | UnsignedVC;
|
|
194
|
-
const expectedWithAlignments = cloneDeep(baseExpected);
|
|
195
|
-
|
|
196
|
-
const rawAlignments = await buildObv3AlignmentsForBoost(boost, domain);
|
|
197
|
-
const jsonLdAlignments = (rawAlignments || []).map(a => ({
|
|
198
|
-
...a,
|
|
199
|
-
type: ['Alignment'],
|
|
200
|
-
}));
|
|
201
|
-
|
|
202
|
-
const addAlignments = (subject: any) => {
|
|
203
|
-
if (!subject) return;
|
|
204
|
-
|
|
205
|
-
if (jsonLdAlignments.length === 0) return;
|
|
206
|
-
|
|
207
|
-
if (subject.achievement) {
|
|
208
|
-
const ach = subject.achievement;
|
|
209
|
-
if (!Array.isArray(ach.alignment))
|
|
210
|
-
ach.alignment = Array.isArray(ach.alignment) ? ach.alignment : [];
|
|
211
|
-
ach.alignment = [...ach.alignment, ...jsonLdAlignments];
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
if (!Array.isArray(subject.alignment))
|
|
215
|
-
subject.alignment = Array.isArray(subject.alignment) ? subject.alignment : [];
|
|
216
|
-
subject.alignment = [...subject.alignment, ...jsonLdAlignments];
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
if (Array.isArray((expectedWithAlignments as any).credentialSubject)) {
|
|
220
|
-
(expectedWithAlignments as any).credentialSubject.forEach(addAlignments);
|
|
221
|
-
} else {
|
|
222
|
-
addAlignments((expectedWithAlignments as any).credentialSubject);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const credAch = (credential as any).credentialSubject.achievement;
|
|
226
|
-
const expectedAch = (expectedWithAlignments as any).credentialSubject?.achievement;
|
|
227
|
-
const baseAch = (baseExpected as any).credentialSubject?.achievement;
|
|
228
|
-
|
|
229
|
-
const matchWithAlignments = isEqual(credAch, expectedAch);
|
|
230
|
-
const matchWithoutAlignments = isEqual(credAch, baseAch);
|
|
231
|
-
|
|
232
|
-
if (!matchWithAlignments) {
|
|
233
|
-
if (matchWithoutAlignments && jsonLdAlignments.length > 0) {
|
|
234
|
-
// Special warning: Accept but inform client to include alignments next time
|
|
235
|
-
console.warn(
|
|
236
|
-
'[OBV3 ALIGNMENTS WARNING] Credential matches boost template without OBv3 alignments. Please update the client to include alignments before issuing.'
|
|
237
|
-
);
|
|
238
|
-
} else {
|
|
239
|
-
console.error(
|
|
240
|
-
'Credential achievement !== expected boost credential achievement (after alignment injection)',
|
|
241
|
-
credAch,
|
|
242
|
-
expectedAch
|
|
243
|
-
);
|
|
244
|
-
return false;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
} catch (_e) {
|
|
248
|
-
// Non-fatal: fallback to legacy comparison
|
|
249
|
-
if (
|
|
250
|
-
!isEqual(
|
|
251
|
-
(credential as any).credentialSubject?.achievement,
|
|
252
|
-
(boostCredential as any).credentialSubject?.achievement
|
|
253
|
-
)
|
|
254
|
-
) {
|
|
255
|
-
console.error(
|
|
256
|
-
'Credential achievement !== boost credential achievement (legacy path)',
|
|
257
|
-
(credential as any).credentialSubject?.achievement,
|
|
258
|
-
(boostCredential as any).credentialSubject?.achievement
|
|
259
|
-
);
|
|
260
|
-
return false;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (!isEqual(credential.display, boostCredential.display)) {
|
|
266
|
-
console.error(
|
|
267
|
-
'Credential display !== boost credential display',
|
|
268
|
-
credential.display,
|
|
269
|
-
boostCredential.display
|
|
270
|
-
);
|
|
271
|
-
return false;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (!isEqual(credential.image, boostCredential.image)) {
|
|
275
|
-
console.error(
|
|
276
|
-
'Credential image !== boost credential image',
|
|
277
|
-
credential.image,
|
|
278
|
-
boostCredential.image
|
|
279
|
-
);
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (!isEqual(credential.attachments, boostCredential.attachments)) {
|
|
284
|
-
console.error(
|
|
285
|
-
'Credential attachments !== boost credential attachments',
|
|
286
|
-
credential.attachments,
|
|
287
|
-
boostCredential.attachments
|
|
288
|
-
);
|
|
289
|
-
return false;
|
|
290
|
-
}
|
|
291
|
-
if (boost && credential) {
|
|
292
|
-
return true;
|
|
293
|
-
}
|
|
294
|
-
return false;
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
export const issueCertifiedBoost = async (
|
|
298
|
-
boost: BoostInstance,
|
|
299
|
-
credential: VC,
|
|
300
|
-
domain: string,
|
|
301
|
-
ownerProfileId: string
|
|
302
|
-
): Promise<VC | JWE | false> => {
|
|
303
|
-
return trace('certification', 'issueCertifiedBoost', async () => {
|
|
304
|
-
const learnCard = await trace('init', 'getLearnCard', () => getLearnCard(undefined, true));
|
|
305
|
-
|
|
306
|
-
let lcnDID = `did:web:${domain}`;
|
|
307
|
-
|
|
308
|
-
try {
|
|
309
|
-
const didDoc = await trace('did', 'resolveDid', () =>
|
|
310
|
-
learnCard.invoke.resolveDid(lcnDID)
|
|
311
|
-
);
|
|
312
|
-
|
|
313
|
-
if (!didDoc) {
|
|
314
|
-
lcnDID = learnCard.id.did();
|
|
315
|
-
}
|
|
316
|
-
} catch (error) {
|
|
317
|
-
if (environment.NODE_ENV !== 'test') {
|
|
318
|
-
console.warn(
|
|
319
|
-
'LCN DID Document is unable to resolve while issuing Certified Boost. Reverting to did:key. Is this a test environment?',
|
|
320
|
-
lcnDID,
|
|
321
|
-
error
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
lcnDID = learnCard.id.did();
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
try {
|
|
328
|
-
const isValid = await traceInternal('verifyCredentialIsDerivedFromBoost', () =>
|
|
329
|
-
verifyCredentialIsDerivedFromBoost(boost, credential, domain)
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
if (isValid) {
|
|
333
|
-
const unsignedCertifiedBoost = await traceInternal(
|
|
334
|
-
'constructCertifiedBoostCredential',
|
|
335
|
-
() => constructCertifiedBoostCredential(boost, credential, domain, lcnDID)
|
|
336
|
-
);
|
|
337
|
-
|
|
338
|
-
await traceInternal('appendBitstringStatusListEntries:certifiedBoost', () =>
|
|
339
|
-
appendBitstringStatusListEntries(unsignedCertifiedBoost, ownerProfileId, domain)
|
|
340
|
-
);
|
|
341
|
-
|
|
342
|
-
// TODO: Encrypt Boost Credential
|
|
343
|
-
const certifiedBoost = await traceCrypto('issueCredential', () =>
|
|
344
|
-
learnCard.invoke.issueCredential(unsignedCertifiedBoost)
|
|
345
|
-
);
|
|
346
|
-
|
|
347
|
-
return certifiedBoost;
|
|
348
|
-
} else {
|
|
349
|
-
console.warn(
|
|
350
|
-
'Credential is not derived from boost',
|
|
351
|
-
boost.dataValues.boost,
|
|
352
|
-
credential
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
} catch (error) {
|
|
356
|
-
console.warn('Could not issue certified boost', error);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return false;
|
|
360
|
-
});
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
export const decryptCredential = async (credential: VC | JWE): Promise<VC | false> => {
|
|
364
|
-
if (!isEncrypted(credential)) {
|
|
365
|
-
return credential;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
return traceCrypto('decryptCredential', async () => {
|
|
369
|
-
const learnCard = await getLearnCard();
|
|
370
|
-
|
|
371
|
-
try {
|
|
372
|
-
const decrypted = await learnCard.invoke.decryptDagJwe<VC>(credential as JWE, [
|
|
373
|
-
learnCard.id.keypair(),
|
|
374
|
-
]);
|
|
375
|
-
|
|
376
|
-
return decrypted || false;
|
|
377
|
-
} catch (error) {
|
|
378
|
-
console.warn('Could not decrypt Boost Credential!', error);
|
|
379
|
-
return false;
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
};
|
|
383
|
-
|
|
384
110
|
export const sendBoost = async ({
|
|
385
111
|
from,
|
|
386
112
|
to,
|
|
@@ -389,7 +115,6 @@ export const sendBoost = async ({
|
|
|
389
115
|
domain,
|
|
390
116
|
skipNotification = false,
|
|
391
117
|
autoAcceptCredential = false,
|
|
392
|
-
skipCertification = false,
|
|
393
118
|
contractTerms,
|
|
394
119
|
metadata,
|
|
395
120
|
activityId,
|
|
@@ -399,11 +124,10 @@ export const sendBoost = async ({
|
|
|
399
124
|
from: CredentialIssuer;
|
|
400
125
|
to: ProfileType;
|
|
401
126
|
boost: BoostInstance;
|
|
402
|
-
credential: VC | JWE;
|
|
127
|
+
credential: VC | JWE | IssuedCredential;
|
|
403
128
|
domain: string;
|
|
404
129
|
skipNotification?: boolean;
|
|
405
130
|
autoAcceptCredential?: boolean;
|
|
406
|
-
skipCertification?: boolean;
|
|
407
131
|
contractTerms?: DbTermsType;
|
|
408
132
|
metadata?: Record<string, unknown>;
|
|
409
133
|
activityId?: string;
|
|
@@ -414,137 +138,70 @@ export const sendBoost = async ({
|
|
|
414
138
|
'boost',
|
|
415
139
|
'sendBoost',
|
|
416
140
|
async () => {
|
|
417
|
-
const decryptedCredential = skipCertification
|
|
418
|
-
? false
|
|
419
|
-
: await decryptCredential(credential);
|
|
420
|
-
let boostUri: string | undefined;
|
|
421
141
|
const sourceBoostUri = getBoostUri(boost.dataValues.id, domain);
|
|
422
142
|
const fromProfile = getIssuerOwnerProfile(from);
|
|
423
143
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
);
|
|
144
|
+
const payload = getIssuedCredentialPayload(credential);
|
|
145
|
+
// Pre-signed credentials may predate their network Boost (send can create
|
|
146
|
+
// one from the signed payload). Preserve them without inventing a signed
|
|
147
|
+
// association; only an explicit conflicting claim is rejected.
|
|
148
|
+
if (
|
|
149
|
+
!isEncrypted(payload) &&
|
|
150
|
+
(payload as VC).boostId !== undefined &&
|
|
151
|
+
(payload as VC).boostId !== sourceBoostUri
|
|
152
|
+
) {
|
|
153
|
+
throw new TRPCError({
|
|
154
|
+
code: 'BAD_REQUEST',
|
|
155
|
+
message: 'Credential boostId must match the Boost being sent.',
|
|
156
|
+
});
|
|
157
|
+
}
|
|
437
158
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const tasks = [
|
|
443
|
-
createBoostInstanceOfRelationship(credentialInstance, boost),
|
|
444
|
-
createSentCredentialRelationship(
|
|
445
|
-
from,
|
|
446
|
-
to,
|
|
447
|
-
credentialInstance,
|
|
448
|
-
metadata,
|
|
449
|
-
activityId,
|
|
450
|
-
integrationId
|
|
451
|
-
),
|
|
452
|
-
];
|
|
453
|
-
|
|
454
|
-
if (listingId) {
|
|
455
|
-
tasks.push(
|
|
456
|
-
createListingSentCredentialRelationship(
|
|
457
|
-
listingId,
|
|
458
|
-
to,
|
|
459
|
-
credentialInstance,
|
|
460
|
-
metadata,
|
|
461
|
-
activityId,
|
|
462
|
-
integrationId
|
|
463
|
-
)
|
|
464
|
-
);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// If this credential is being issued via a contract, create that relationship
|
|
468
|
-
if (contractTerms) {
|
|
469
|
-
tasks.push(
|
|
470
|
-
createCredentialIssuedViaContractRelationship(
|
|
471
|
-
credentialInstance,
|
|
472
|
-
contractTerms
|
|
473
|
-
)
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
await traceDb('createRelationships', () => Promise.all(tasks));
|
|
478
|
-
|
|
479
|
-
if (autoAcceptCredential) {
|
|
480
|
-
await acceptCredential(
|
|
481
|
-
to,
|
|
482
|
-
getCredentialUri(credentialInstance.id, domain),
|
|
483
|
-
{
|
|
484
|
-
skipNotification,
|
|
485
|
-
}
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
boostUri = getCredentialUri(credentialInstance.id, domain);
|
|
490
|
-
|
|
491
|
-
if (environment.NODE_ENV !== 'test') {
|
|
492
|
-
console.log('🚀 sendBoost:boost certified', boostUri);
|
|
493
|
-
}
|
|
494
|
-
} else {
|
|
495
|
-
throw new Error('Credential does not match boost template.');
|
|
496
|
-
}
|
|
497
|
-
} else {
|
|
498
|
-
// TODO: Should we warn them if they send a credential that can't be decrypted?
|
|
499
|
-
const credentialInstance = await traceDb('storeCredential', () =>
|
|
500
|
-
storeCredential(credential)
|
|
501
|
-
);
|
|
159
|
+
// Preserve the issuer's payload without decrypting or counter-signing it.
|
|
160
|
+
const credentialInstance = await traceDb('storeCredential', () =>
|
|
161
|
+
storeCredential(credential)
|
|
162
|
+
);
|
|
502
163
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
164
|
+
const tasks = [
|
|
165
|
+
createBoostInstanceOfRelationship(credentialInstance, boost),
|
|
166
|
+
createSentCredentialRelationship(
|
|
167
|
+
from,
|
|
168
|
+
to,
|
|
169
|
+
credentialInstance,
|
|
170
|
+
metadata,
|
|
171
|
+
activityId,
|
|
172
|
+
integrationId
|
|
173
|
+
),
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
if (listingId) {
|
|
177
|
+
tasks.push(
|
|
178
|
+
createListingSentCredentialRelationship(
|
|
179
|
+
listingId,
|
|
507
180
|
to,
|
|
508
181
|
credentialInstance,
|
|
509
182
|
metadata,
|
|
510
183
|
activityId,
|
|
511
184
|
integrationId
|
|
512
|
-
)
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
if (listingId) {
|
|
516
|
-
tasks.push(
|
|
517
|
-
createListingSentCredentialRelationship(
|
|
518
|
-
listingId,
|
|
519
|
-
to,
|
|
520
|
-
credentialInstance,
|
|
521
|
-
metadata,
|
|
522
|
-
activityId,
|
|
523
|
-
integrationId
|
|
524
|
-
)
|
|
525
|
-
);
|
|
526
|
-
}
|
|
185
|
+
)
|
|
186
|
+
);
|
|
187
|
+
}
|
|
527
188
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
)
|
|
534
|
-
);
|
|
535
|
-
}
|
|
189
|
+
if (contractTerms) {
|
|
190
|
+
tasks.push(
|
|
191
|
+
createCredentialIssuedViaContractRelationship(credentialInstance, contractTerms)
|
|
192
|
+
);
|
|
193
|
+
}
|
|
536
194
|
|
|
537
|
-
|
|
195
|
+
await traceDb('createRelationships', () => Promise.all(tasks));
|
|
538
196
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
boostUri = getCredentialUri(credentialInstance.id, domain);
|
|
197
|
+
if (autoAcceptCredential) {
|
|
198
|
+
await acceptCredential(to, getCredentialUri(credentialInstance.id, domain), {
|
|
199
|
+
skipNotification,
|
|
200
|
+
});
|
|
546
201
|
}
|
|
547
202
|
|
|
203
|
+
const boostUri = getCredentialUri(credentialInstance.id, domain);
|
|
204
|
+
|
|
548
205
|
if (typeof boostUri === 'string') {
|
|
549
206
|
if (!skipNotification) {
|
|
550
207
|
// LC-1644: fire-and-forget the BOOST_RECEIVED notification enqueue.
|
|
@@ -591,36 +248,6 @@ export const sendBoost = async ({
|
|
|
591
248
|
);
|
|
592
249
|
};
|
|
593
250
|
|
|
594
|
-
export const constructCertifiedBoostCredential = async (
|
|
595
|
-
boost: BoostInstance,
|
|
596
|
-
credential: VC | JWE,
|
|
597
|
-
domain: string,
|
|
598
|
-
issuerDid: string
|
|
599
|
-
): Promise<UnsignedVC> => {
|
|
600
|
-
const currentDate = new Date()?.toISOString();
|
|
601
|
-
|
|
602
|
-
const boostId = boost?.dataValues?.id;
|
|
603
|
-
const boostURI = getBoostUri(boostId, domain);
|
|
604
|
-
|
|
605
|
-
const isVC2 = isVC2Format(credential);
|
|
606
|
-
|
|
607
|
-
return {
|
|
608
|
-
'@context': [
|
|
609
|
-
isVC2
|
|
610
|
-
? 'https://www.w3.org/ns/credentials/v2'
|
|
611
|
-
: 'https://www.w3.org/2018/credentials/v1',
|
|
612
|
-
'https://ctx.learncard.com/boosts/1.0.1.json',
|
|
613
|
-
],
|
|
614
|
-
id: `urn:uuid:${uuidv4()}`,
|
|
615
|
-
type: ['VerifiableCredential', 'CertifiedBoostCredential'],
|
|
616
|
-
issuer: issuerDid,
|
|
617
|
-
...(isVC2 ? { validFrom: currentDate } : { issuanceDate: currentDate }),
|
|
618
|
-
credentialSubject: { id: issuerDid },
|
|
619
|
-
boostId: boostURI,
|
|
620
|
-
boostCredential: credential,
|
|
621
|
-
};
|
|
622
|
-
};
|
|
623
|
-
|
|
624
251
|
export const issueClaimLinkBoost = async (
|
|
625
252
|
boost: BoostInstance,
|
|
626
253
|
domain: string,
|
|
@@ -638,14 +265,14 @@ export const issueClaimLinkBoost = async (
|
|
|
638
265
|
if (Array.isArray(boostCredential.credentialSubject)) {
|
|
639
266
|
boostCredential.credentialSubject = boostCredential.credentialSubject.map(subject => ({
|
|
640
267
|
...subject,
|
|
641
|
-
id:
|
|
268
|
+
id: getDidWeb(domain, to.profileId),
|
|
642
269
|
}));
|
|
643
270
|
} else {
|
|
644
271
|
boostCredential.credentialSubject.id = getDidWeb(domain, to.profileId);
|
|
645
272
|
}
|
|
646
273
|
|
|
647
274
|
// Embed the boostURI into the boost credential for verification purposes.
|
|
648
|
-
if (boostCredential
|
|
275
|
+
if (boostCredential.type.includes('BoostCredential')) {
|
|
649
276
|
boostCredential.boostId = boostURI;
|
|
650
277
|
}
|
|
651
278
|
|
|
@@ -657,14 +284,8 @@ export const issueClaimLinkBoost = async (
|
|
|
657
284
|
await appendBitstringStatusListEntries(boostCredential, fromProfile.profileId, domain),
|
|
658
285
|
signingAuthorityForUser,
|
|
659
286
|
domain,
|
|
660
|
-
|
|
287
|
+
true
|
|
661
288
|
);
|
|
662
|
-
// TODO: encrypt vc?
|
|
663
|
-
|
|
664
|
-
// const lcnDid = await client.utilities.getDid.query();
|
|
665
|
-
|
|
666
|
-
// const credential = await _learnCard.invoke
|
|
667
|
-
// .createDagJwe(vc, [userData.did, targetProfile.did, lcnDid]);
|
|
668
289
|
|
|
669
290
|
return sendBoost({
|
|
670
291
|
from,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const environment = vi.hoisted(() => ({
|
|
4
|
+
CLIENT_APP_DOMAIN_NAME: undefined as string | undefined,
|
|
5
|
+
CLIENT_APP_PORT: undefined as number | undefined,
|
|
6
|
+
PORT: 4000,
|
|
7
|
+
IS_OFFLINE: true,
|
|
8
|
+
}));
|
|
9
|
+
vi.mock('@environment', () => ({ environment }));
|
|
10
|
+
vi.mock('@cache', () => ({ default: {} }));
|
|
11
|
+
|
|
12
|
+
import { generateClaimUrl } from './contact-method.helpers';
|
|
13
|
+
|
|
14
|
+
describe('inbox claim links', () => {
|
|
15
|
+
it('opens the default frontend instead of the backend port', () => {
|
|
16
|
+
expect(generateClaimUrl('test-token')).toBe(
|
|
17
|
+
'http://localhost:3000/interactions/inbox-claim/test-token?iuv=1'
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('uses the configured frontend port locally', () => {
|
|
22
|
+
environment.CLIENT_APP_PORT = 3100;
|
|
23
|
+
try {
|
|
24
|
+
expect(generateClaimUrl('test-token')).toContain('http://localhost:3100/');
|
|
25
|
+
} finally {
|
|
26
|
+
environment.CLIENT_APP_PORT = undefined;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('honors a configured app domain locally and keeps production HTTPS', () => {
|
|
31
|
+
environment.CLIENT_APP_DOMAIN_NAME = 'app.example.org';
|
|
32
|
+
try {
|
|
33
|
+
expect(generateClaimUrl('test-token')).toContain('http://app.example.org/');
|
|
34
|
+
environment.IS_OFFLINE = false;
|
|
35
|
+
expect(generateClaimUrl('test-token')).toBe(
|
|
36
|
+
'https://app.example.org/interactions/inbox-claim/test-token?iuv=1'
|
|
37
|
+
);
|
|
38
|
+
} finally {
|
|
39
|
+
environment.IS_OFFLINE = true;
|
|
40
|
+
environment.CLIENT_APP_DOMAIN_NAME = undefined;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -110,10 +110,9 @@ export const markInboxClaimTokenAsUsed = async (token: string): Promise<boolean>
|
|
|
110
110
|
|
|
111
111
|
export const generateClaimUrl = (token: string): string => {
|
|
112
112
|
const domainName = environment.CLIENT_APP_DOMAIN_NAME;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
: domainName;
|
|
113
|
+
// Claim links open the frontend, not the brain-service HTTP port. Honor an
|
|
114
|
+
// explicitly configured app domain in local development as well.
|
|
115
|
+
const domain = domainName || `localhost:${environment.CLIENT_APP_PORT || 3000}`;
|
|
117
116
|
|
|
118
117
|
const protocol = environment.IS_OFFLINE ? 'http' : 'https';
|
|
119
118
|
|