@learncard/network-plugin 2.13.16 → 2.13.17
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/dist/lcn-plugin.cjs.development.cjs +75 -13
- package/dist/lcn-plugin.cjs.development.cjs.map +3 -3
- package/dist/lcn-plugin.cjs.production.min.cjs +34 -34
- package/dist/lcn-plugin.cjs.production.min.cjs.map +3 -3
- package/dist/lcn-plugin.esm.js +75 -13
- package/dist/lcn-plugin.esm.js.map +3 -3
- package/dist/plugin.d.ts +26 -6
- package/dist/plugin.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/plugin.ts +172 -32
|
@@ -17545,6 +17545,56 @@ mustache.Writer = Writer;
|
|
|
17545
17545
|
var mustache_default = mustache;
|
|
17546
17546
|
|
|
17547
17547
|
// src/plugin.ts
|
|
17548
|
+
var LEARNCARD_DOMAIN_SUFFIXES = [".learncard.com", ".learncard.app", ".learncard.ai"];
|
|
17549
|
+
var isLearnCardDomain = /* @__PURE__ */ __name((host) => {
|
|
17550
|
+
return LEARNCARD_DOMAIN_SUFFIXES.some(
|
|
17551
|
+
(suffix) => host === suffix.replace(/^\./, "") || host.endsWith(suffix)
|
|
17552
|
+
);
|
|
17553
|
+
}, "isLearnCardDomain");
|
|
17554
|
+
var isLocalhostHost = /* @__PURE__ */ __name((host) => {
|
|
17555
|
+
const lower = host.toLowerCase();
|
|
17556
|
+
return lower === "localhost" || lower === "127.0.0.1";
|
|
17557
|
+
}, "isLocalhostHost");
|
|
17558
|
+
var validateFederationUrl = /* @__PURE__ */ __name((userProvidedUrl, config2) => {
|
|
17559
|
+
let parsed;
|
|
17560
|
+
try {
|
|
17561
|
+
parsed = new URL(userProvidedUrl);
|
|
17562
|
+
} catch {
|
|
17563
|
+
throw new Error(`Invalid federation endpoint URL: ${userProvidedUrl}`);
|
|
17564
|
+
}
|
|
17565
|
+
if (config2.serviceOrigin) {
|
|
17566
|
+
const endpointOrigin = parsed.origin;
|
|
17567
|
+
if (endpointOrigin === config2.serviceOrigin) {
|
|
17568
|
+
return parsed.href;
|
|
17569
|
+
}
|
|
17570
|
+
}
|
|
17571
|
+
const host = parsed.hostname.toLowerCase();
|
|
17572
|
+
const isLocalhost = isLocalhostHost(host);
|
|
17573
|
+
if (isLocalhost) {
|
|
17574
|
+
if (!config2.allowLocalhostFederation) {
|
|
17575
|
+
throw new Error(
|
|
17576
|
+
`Localhost federation is disabled. Set allowLocalhostFederation: true for development.`
|
|
17577
|
+
);
|
|
17578
|
+
}
|
|
17579
|
+
if (parsed.protocol === "https:") {
|
|
17580
|
+
parsed.protocol = "http:";
|
|
17581
|
+
}
|
|
17582
|
+
return parsed.href;
|
|
17583
|
+
}
|
|
17584
|
+
if (parsed.protocol !== "https:") {
|
|
17585
|
+
throw new Error(`Federation requires HTTPS for non-localhost hosts: ${userProvidedUrl}`);
|
|
17586
|
+
}
|
|
17587
|
+
if (config2.trustedFederationHosts !== void 0) {
|
|
17588
|
+
const trustedHosts = new Set(config2.trustedFederationHosts);
|
|
17589
|
+
const allowAnyHost = trustedHosts.has("*");
|
|
17590
|
+
if (!allowAnyHost && !isLearnCardDomain(host) && !trustedHosts.has(host)) {
|
|
17591
|
+
throw new Error(
|
|
17592
|
+
`Federation host '${host}' is not trusted. Add it to trustedFederationHosts or use '*' to allow any host.`
|
|
17593
|
+
);
|
|
17594
|
+
}
|
|
17595
|
+
}
|
|
17596
|
+
return parsed.href;
|
|
17597
|
+
}, "validateFederationUrl");
|
|
17548
17598
|
var uint8ArrayToBase64Url = /* @__PURE__ */ __name((bytes) => {
|
|
17549
17599
|
let binary = "";
|
|
17550
17600
|
for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
|
|
@@ -17638,8 +17688,20 @@ var issueCredentialWithNetworkStatus = /* @__PURE__ */ __name(async (learnCard,
|
|
|
17638
17688
|
}, "issueCredentialWithNetworkStatus");
|
|
17639
17689
|
async function getLearnCardNetworkPlugin(learnCard, url2, apiTokenOrOptions, options) {
|
|
17640
17690
|
const apiToken = typeof apiTokenOrOptions === "string" ? apiTokenOrOptions : void 0;
|
|
17641
|
-
const
|
|
17642
|
-
const
|
|
17691
|
+
const resolvedOptions = (typeof apiTokenOrOptions === "object" ? apiTokenOrOptions : options) ?? {};
|
|
17692
|
+
const {
|
|
17693
|
+
guardianApprovalGetter,
|
|
17694
|
+
extraHeaders,
|
|
17695
|
+
trustedFederationHosts,
|
|
17696
|
+
allowLocalhostFederation
|
|
17697
|
+
} = resolvedOptions;
|
|
17698
|
+
const serviceUrl = new URL(url2);
|
|
17699
|
+
const serviceIsLocalhost = isLocalhostHost(serviceUrl.hostname);
|
|
17700
|
+
const federationConfig = {
|
|
17701
|
+
trustedFederationHosts,
|
|
17702
|
+
allowLocalhostFederation: allowLocalhostFederation ?? serviceIsLocalhost,
|
|
17703
|
+
serviceOrigin: serviceUrl.origin
|
|
17704
|
+
};
|
|
17643
17705
|
let did = "";
|
|
17644
17706
|
try {
|
|
17645
17707
|
const idPlane = learnCard?.id;
|
|
@@ -17708,8 +17770,8 @@ async function getLearnCardNetworkPlugin(learnCard, url2, apiTokenOrOptions, opt
|
|
|
17708
17770
|
}
|
|
17709
17771
|
}, "getBoostTemplateForIssuance");
|
|
17710
17772
|
const getInboxEndpointForDid = /* @__PURE__ */ __name(async (_learnCard, recipientDid) => {
|
|
17711
|
-
const
|
|
17712
|
-
const serviceDomain = `${
|
|
17773
|
+
const serviceUrl2 = new URL(url2);
|
|
17774
|
+
const serviceDomain = `${serviceUrl2.hostname}${serviceUrl2.port ? `%3A${serviceUrl2.port}` : ""}`;
|
|
17713
17775
|
const localServiceDid = `did:web:${serviceDomain}`;
|
|
17714
17776
|
const getInferredServiceDid = /* @__PURE__ */ __name((did2) => {
|
|
17715
17777
|
if (!did2.startsWith("did:web:")) return null;
|
|
@@ -17730,12 +17792,12 @@ async function getLearnCardNetworkPlugin(learnCard, url2, apiTokenOrOptions, opt
|
|
|
17730
17792
|
}), "getInboxService");
|
|
17731
17793
|
const inferredServiceDid = getInferredServiceDid(recipientDid);
|
|
17732
17794
|
if (inferredServiceDid === localServiceDid) {
|
|
17733
|
-
return `${
|
|
17795
|
+
return `${serviceUrl2.origin}/api/inbox/receive`;
|
|
17734
17796
|
}
|
|
17735
17797
|
const didDoc = await _learnCard.invoke.resolveDid(recipientDid);
|
|
17736
17798
|
const inboxService = getInboxService(didDoc);
|
|
17737
17799
|
if ((inboxService?.serviceDid || inferredServiceDid) === localServiceDid) {
|
|
17738
|
-
return `${
|
|
17800
|
+
return `${serviceUrl2.origin}/api/inbox/receive`;
|
|
17739
17801
|
}
|
|
17740
17802
|
if (!inboxService?.serviceEndpoint) {
|
|
17741
17803
|
throw new Error(
|
|
@@ -17897,7 +17959,8 @@ async function getLearnCardNetworkPlugin(learnCard, url2, apiTokenOrOptions, opt
|
|
|
17897
17959
|
return client.profileManager.updateProfileManager.mutate(manager);
|
|
17898
17960
|
}, "updateProfileManagerProfile"),
|
|
17899
17961
|
deleteProfile: /* @__PURE__ */ __name(async () => {
|
|
17900
|
-
|
|
17962
|
+
await initialQuery;
|
|
17963
|
+
if (!apiToken) await ensureUser();
|
|
17901
17964
|
const result = await client.profile.deleteProfile.mutate();
|
|
17902
17965
|
if (result) {
|
|
17903
17966
|
userData = void 0;
|
|
@@ -18048,11 +18111,10 @@ async function getLearnCardNetworkPlugin(learnCard, url2, apiTokenOrOptions, opt
|
|
|
18048
18111
|
proofFormat: "jwt",
|
|
18049
18112
|
challenge: `inbox-federation-${crypto.randomUUID()}`
|
|
18050
18113
|
});
|
|
18051
|
-
|
|
18052
|
-
if (receiveUrl.includes("localhost")) {
|
|
18053
|
-
receiveUrl = receiveUrl.replace("https://", "http://");
|
|
18054
|
-
}
|
|
18114
|
+
const receiveUrl = validateFederationUrl(inboxEndpoint, federationConfig);
|
|
18055
18115
|
const response = await fetch(receiveUrl, {
|
|
18116
|
+
// A redirect must not bypass endpoint validation or forward credentials.
|
|
18117
|
+
redirect: "error",
|
|
18056
18118
|
method: "POST",
|
|
18057
18119
|
headers: {
|
|
18058
18120
|
"Content-Type": "application/json",
|
|
@@ -18527,8 +18589,8 @@ async function getLearnCardNetworkPlugin(learnCard, url2, apiTokenOrOptions, opt
|
|
|
18527
18589
|
const isDid = recipient.startsWith("did:");
|
|
18528
18590
|
const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(recipient);
|
|
18529
18591
|
const isPhone = /^\+?[\d\s-]{10,}$/.test(recipient.replace(/[\s-]/g, ""));
|
|
18530
|
-
const
|
|
18531
|
-
const serviceDomain = `${
|
|
18592
|
+
const serviceUrl2 = new URL(url2);
|
|
18593
|
+
const serviceDomain = `${serviceUrl2.hostname}${serviceUrl2.port ? `%3A${serviceUrl2.port}` : ""}`;
|
|
18532
18594
|
const recipientDomain = recipient.startsWith("did:web:") ? recipient.split(":")[2] : void 0;
|
|
18533
18595
|
const isRemoteDidWebRecipient = recipient.startsWith("did:web:") && recipientDomain !== serviceDomain;
|
|
18534
18596
|
if (isRemoteDidWebRecipient && input.templateUri) {
|