@learncard/open-badge-v2-plugin 1.0.12 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/open-badge-v2-plugin.cjs.development.js +7 -8
- package/dist/open-badge-v2-plugin.cjs.development.js.map +1 -1
- package/dist/open-badge-v2-plugin.cjs.production.min.js +1 -1
- package/dist/open-badge-v2-plugin.cjs.production.min.js.map +2 -2
- package/dist/open-badge-v2-plugin.esm.js +4 -5
- package/dist/open-badge-v2-plugin.esm.js.map +1 -1
- package/package.json +9 -9
|
@@ -19,12 +19,12 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
19
19
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
20
|
|
|
21
21
|
// src/index.ts
|
|
22
|
-
var
|
|
23
|
-
__export(
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
24
|
OBV2_WRAPPER_CONTEXT_URL: () => OBV2_WRAPPER_CONTEXT_URL,
|
|
25
25
|
openBadgeV2Plugin: () => openBadgeV2Plugin
|
|
26
26
|
});
|
|
27
|
-
module.exports = __toCommonJS(
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
28
|
|
|
29
29
|
// src/types.ts
|
|
30
30
|
var OBV2_WRAPPER_CONTEXT_URL = "https://ctx.learncard.com/wrappers/obv2/1.0.0.json";
|
|
@@ -101,8 +101,7 @@ async function getObv2Assertion(input) {
|
|
|
101
101
|
}
|
|
102
102
|
__name(getObv2Assertion, "getObv2Assertion");
|
|
103
103
|
function validateObv2(obv2) {
|
|
104
|
-
if (!obv2 || typeof obv2 !== "object")
|
|
105
|
-
throw new Error("wrapOpenBadgeV2: Missing assertion");
|
|
104
|
+
if (!obv2 || typeof obv2 !== "object") throw new Error("wrapOpenBadgeV2: Missing assertion");
|
|
106
105
|
const id = obv2.id;
|
|
107
106
|
if (typeof id !== "string" || !id.length)
|
|
108
107
|
throw new Error("wrapOpenBadgeV2: assertion.id is required");
|
|
@@ -120,7 +119,7 @@ var openBadgeV2Plugin = /* @__PURE__ */ __name((learnCard) => ({
|
|
|
120
119
|
displayName: "OpenBadge v2 Wrapper",
|
|
121
120
|
description: "Wrap legacy OpenBadge v2.0 assertions into self-issued Verifiable Credentials",
|
|
122
121
|
methods: {
|
|
123
|
-
wrapOpenBadgeV2: async (_learnCard, obv2Assertion) => {
|
|
122
|
+
wrapOpenBadgeV2: /* @__PURE__ */ __name(async (_learnCard, obv2Assertion) => {
|
|
124
123
|
const issuerDid = learnCard.id.did();
|
|
125
124
|
const obv2 = await getObv2Assertion(obv2Assertion);
|
|
126
125
|
validateObv2(obv2);
|
|
@@ -129,14 +128,14 @@ var openBadgeV2Plugin = /* @__PURE__ */ __name((learnCard) => ({
|
|
|
129
128
|
id: `urn:uuid:${_learnCard.invoke.crypto().randomUUID()}`,
|
|
130
129
|
type: ["VerifiableCredential", "LegacyOpenBadgeCredential"],
|
|
131
130
|
issuer: issuerDid,
|
|
132
|
-
validFrom: new Date().toISOString(),
|
|
131
|
+
validFrom: (/* @__PURE__ */ new Date()).toISOString(),
|
|
133
132
|
credentialSubject: {
|
|
134
133
|
id: issuerDid
|
|
135
134
|
},
|
|
136
135
|
legacyAssertion: obv2
|
|
137
136
|
};
|
|
138
137
|
return learnCard.invoke.issueCredential(unsigned, { proofPurpose: "assertionMethod" });
|
|
139
|
-
}
|
|
138
|
+
}, "wrapOpenBadgeV2")
|
|
140
139
|
}
|
|
141
140
|
}), "openBadgeV2Plugin");
|
|
142
141
|
//# sourceMappingURL=open-badge-v2-plugin.cjs.development.js.map
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts", "../src/types.ts", "../src/plugin.ts"],
|
|
4
4
|
"sourcesContent": ["export { openBadgeV2Plugin } from './plugin';\nexport * from './types';\n", "import { Plugin } from '@learncard/core';\nimport { VC } from '@learncard/types';\nimport { VCPluginMethods } from '@learncard/vc-plugin';\nimport { CryptoPluginMethods } from '@learncard/crypto-plugin';\n\nexport type OpenBadgeV2PluginDependentMethods = VCPluginMethods & CryptoPluginMethods;\n\nexport type OpenBadgeV2PluginMethods = {\n /**\n * Wrap a legacy OpenBadge v2.0 assertion (object or URL) into a self-issued VC\n */\n wrapOpenBadgeV2: (obv2Assertion: object | string) => Promise<VC>;\n};\n\nexport type OpenBadgeV2Plugin = Plugin<\n 'OpenBadgeV2',\n any,\n OpenBadgeV2PluginMethods,\n 'id',\n OpenBadgeV2PluginDependentMethods\n>;\n\nexport const OBV2_WRAPPER_CONTEXT_URL = 'https://ctx.learncard.com/wrappers/obv2/1.0.0.json';\n", "import { LearnCard } from '@learncard/core';\nimport { UnsignedVC, VC } from '@learncard/types';\n\nimport {\n OpenBadgeV2Plugin,\n OBV2_WRAPPER_CONTEXT_URL,\n OpenBadgeV2PluginDependentMethods,\n} from './types';\n\nconst VC_V2_CONTEXT = 'https://www.w3.org/ns/credentials/v2';\n\nconst isUrl = (value: string): boolean =>\n /^(https?:)?\\/\\//i.test(value) || value.startsWith('ipfs://') || value.startsWith('ipns://');\n\nconst isValidAndSafeUrl = (url: string): void => {\n try {\n const parsedUrl = new URL(url);\n\n // Only allow http, https, ipfs, ipns protocols\n const allowedProtocols = ['https:', 'http:', 'ipfs:', 'ipns:'];\n if (!allowedProtocols.includes(parsedUrl.protocol)) {\n throw new Error(\n `wrapOpenBadgeV2: Protocol '${parsedUrl.protocol}' is not allowed. Only HTTP(S), IPFS, and IPNS are supported`\n );\n }\n\n // Skip internal network checks for IPFS/IPNS as they don't use traditional IP addresses\n if (parsedUrl.protocol === 'ipfs:' || parsedUrl.protocol === 'ipns:') {\n return;\n }\n\n // Block localhost and internal networks for HTTP(S)\n const hostname = parsedUrl.hostname.toLowerCase();\n\n // Block localhost variations\n if (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '0.0.0.0' ||\n hostname === '::1'\n ) {\n throw new Error('wrapOpenBadgeV2: Access to localhost is not allowed');\n }\n\n // Block private IP ranges (RFC 1918)\n if (\n hostname.startsWith('10.') ||\n hostname.startsWith('192.168.') ||\n (hostname.startsWith('172.') && /^172\\.(1[6-9]|2[0-9]|3[0-1])\\./.test(hostname))\n ) {\n throw new Error('wrapOpenBadgeV2: Access to private IP ranges is not allowed');\n }\n\n // Block link-local addresses\n if (hostname.startsWith('169.254.')) {\n throw new Error('wrapOpenBadgeV2: Access to link-local addresses is not allowed');\n }\n } catch (error) {\n if (error instanceof TypeError) {\n throw new Error('wrapOpenBadgeV2: Invalid URL format');\n }\n throw error;\n }\n};\n\nasync function getObv2Assertion(input: object | string): Promise<Record<string, any>> {\n if (typeof input === 'string') {\n // Treat string as URL. If it looks like raw JSON, try to parse for developer ergonomics\n const trimmed = input.trim();\n if (!isUrl(trimmed) && (trimmed.startsWith('{') || trimmed.startsWith('['))) {\n try {\n return JSON.parse(trimmed);\n } catch (e) {\n throw new Error('wrapOpenBadgeV2: Provided string is not a valid URL or JSON');\n }\n }\n\n // Validate URL for security\n isValidAndSafeUrl(trimmed);\n\n try {\n // Add timeout to prevent hanging requests (10 seconds)\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000);\n\n const res = await fetch(trimmed, {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!res.ok)\n throw new Error(`wrapOpenBadgeV2: Failed to fetch OBv2 assertion: ${res.status}`);\n\n return await res.json();\n } catch (error: any) {\n if (error instanceof TypeError) {\n throw new Error(\n `wrapOpenBadgeV2: Network error while fetching OBv2 assertion: ${error.message}`\n );\n }\n if (error?.name === 'AbortError') {\n throw new Error('wrapOpenBadgeV2: Request timed out after 10 seconds');\n }\n throw error;\n }\n }\n\n // Add basic type validation before assertion\n if (typeof input !== 'object' || input === null) {\n throw new Error('wrapOpenBadgeV2: Input must be a valid object');\n }\n\n return input as Record<string, any>;\n}\n\nfunction validateObv2(obv2: Record<string, any>): void {\n if (!obv2 || typeof obv2 !== 'object') throw new Error('wrapOpenBadgeV2: Missing assertion');\n\n const id = obv2.id;\n if (typeof id !== 'string' || !id.length)\n throw new Error('wrapOpenBadgeV2: assertion.id is required');\n\n const t = obv2.type;\n const hasAssertionType =\n (typeof t === 'string' && t === 'Assertion') ||\n (Array.isArray(t) && t.includes('Assertion'));\n if (!hasAssertionType)\n throw new Error(\"wrapOpenBadgeV2: assertion.type must include 'Assertion'\");\n\n if (!obv2.issuedOn || typeof obv2.issuedOn !== 'string') {\n throw new Error('wrapOpenBadgeV2: assertion.issuedOn (string) is required');\n }\n}\n\nexport const openBadgeV2Plugin = (\n learnCard: LearnCard<any, 'id', OpenBadgeV2PluginDependentMethods>\n): OpenBadgeV2Plugin => ({\n name: 'OpenBadgeV2',\n displayName: 'OpenBadge v2 Wrapper',\n description: 'Wrap legacy OpenBadge v2.0 assertions into self-issued Verifiable Credentials',\n methods: {\n wrapOpenBadgeV2: async (_learnCard, obv2Assertion: object | string): Promise<VC> => {\n const issuerDid = learnCard.id.did();\n\n const obv2 = await getObv2Assertion(obv2Assertion);\n validateObv2(obv2);\n\n const unsigned: UnsignedVC = {\n '@context': [VC_V2_CONTEXT, OBV2_WRAPPER_CONTEXT_URL],\n id: `urn:uuid:${_learnCard.invoke.crypto().randomUUID()}`,\n type: ['VerifiableCredential', 'LegacyOpenBadgeCredential'],\n issuer: issuerDid,\n validFrom: new Date().toISOString(),\n credentialSubject: {\n id: issuerDid,\n },\n legacyAssertion: obv2,\n };\n\n return learnCard.invoke.issueCredential(unsigned, { proofPurpose: 'assertionMethod' });\n },\n },\n});\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsBO,IAAM,2BAA2B;;;ACbxC,IAAM,gBAAgB;AAEtB,IAAM,QAAQ,wBAAC,UACX,mBAAmB,KAAK,KAAK,KAAK,MAAM,WAAW,SAAS,KAAK,MAAM,WAAW,SAAS,GADjF;AAGd,IAAM,oBAAoB,wBAAC,QAAsB;AAC7C,MAAI;AACA,UAAM,YAAY,IAAI,IAAI,GAAG;AAG7B,UAAM,mBAAmB,CAAC,UAAU,SAAS,SAAS,OAAO;AAC7D,QAAI,CAAC,iBAAiB,SAAS,UAAU,QAAQ,GAAG;AAChD,YAAM,IAAI;AAAA,QACN,8BAA8B,UAAU;AAAA,
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var i=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var l=Object.getOwnPropertyNames;var c=Object.prototype.hasOwnProperty;var s=(r,e)=>i(r,"name",{value:e,configurable:!0});var w=(r,e)=>{for(var t in e)i(r,t,{get:e[t],enumerable:!0})},h=(r,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of l(e))!c.call(r,n)&&n!==t&&i(r,n,{get:()=>e[n],enumerable:!(o=g(e,n))||o.enumerable});return r};var f=r=>h(i({},"__esModule",{value:!0}),r);var B={};w(B,{OBV2_WRAPPER_CONTEXT_URL:()=>a,openBadgeV2Plugin:()=>d});module.exports=f(B);var a="https://ctx.learncard.com/wrappers/obv2/1.0.0.json";var u="https://www.w3.org/ns/credentials/v2",O=s(r=>/^(https?:)?\/\//i.test(r)||r.startsWith("ipfs://")||r.startsWith("ipns://"),"isUrl"),V=s(r=>{try{let e=new URL(r);if(!["https:","http:","ipfs:","ipns:"].includes(e.protocol))throw new Error(`wrapOpenBadgeV2: Protocol '${e.protocol}' is not allowed. Only HTTP(S), IPFS, and IPNS are supported`);if(e.protocol==="ipfs:"||e.protocol==="ipns:")return;let o=e.hostname.toLowerCase();if(o==="localhost"||o==="127.0.0.1"||o==="0.0.0.0"||o==="::1")throw new Error("wrapOpenBadgeV2: Access to localhost is not allowed");if(o.startsWith("10.")||o.startsWith("192.168.")||o.startsWith("172.")&&/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(o))throw new Error("wrapOpenBadgeV2: Access to private IP ranges is not allowed");if(o.startsWith("169.254."))throw new Error("wrapOpenBadgeV2: Access to link-local addresses is not allowed")}catch(e){throw e instanceof TypeError?new Error("wrapOpenBadgeV2: Invalid URL format"):e}},"isValidAndSafeUrl");async function m(r){if(typeof r=="string"){let e=r.trim();if(!O(e)&&(e.startsWith("{")||e.startsWith("[")))try{return JSON.parse(e)}catch{throw new Error("wrapOpenBadgeV2: Provided string is not a valid URL or JSON")}V(e);try{let t=new AbortController,o=setTimeout(()=>t.abort(),1e4),n=await fetch(e,{signal:t.signal});if(clearTimeout(o),!n.ok)throw new Error(`wrapOpenBadgeV2: Failed to fetch OBv2 assertion: ${n.status}`);return await n.json()}catch(t){throw t instanceof TypeError?new Error(`wrapOpenBadgeV2: Network error while fetching OBv2 assertion: ${t.message}`):t?.name==="AbortError"?new Error("wrapOpenBadgeV2: Request timed out after 10 seconds"):t}}if(typeof r!="object"||r===null)throw new Error("wrapOpenBadgeV2: Input must be a valid object");return r}s(m,"getObv2Assertion");function y(r){if(!r||typeof r!="object")throw new Error("wrapOpenBadgeV2: Missing assertion");let e=r.id;if(typeof e!="string"||!e.length)throw new Error("wrapOpenBadgeV2: assertion.id is required");let t=r.type;if(!(typeof t=="string"&&t==="Assertion"||Array.isArray(t)&&t.includes("Assertion")))throw new Error("wrapOpenBadgeV2: assertion.type must include 'Assertion'");if(!r.issuedOn||typeof r.issuedOn!="string")throw new Error("wrapOpenBadgeV2: assertion.issuedOn (string) is required")}s(y,"validateObv2");var d=s(r=>({name:"OpenBadgeV2",displayName:"OpenBadge v2 Wrapper",description:"Wrap legacy OpenBadge v2.0 assertions into self-issued Verifiable Credentials",methods:{wrapOpenBadgeV2:async(e,t)=>{let o=r.id.did(),n=await m(t);y(n);let p={"@context":[u,a],id:`urn:uuid:${e.invoke.crypto().randomUUID()}`,type:["VerifiableCredential","LegacyOpenBadgeCredential"],issuer:o,validFrom:new Date().toISOString(),credentialSubject:{id:o},legacyAssertion:n};return r.invoke.issueCredential(p,{proofPurpose:"assertionMethod"})}}}),"openBadgeV2Plugin");
|
|
1
|
+
"use strict";var i=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var l=Object.getOwnPropertyNames;var c=Object.prototype.hasOwnProperty;var s=(r,e)=>i(r,"name",{value:e,configurable:!0});var w=(r,e)=>{for(var t in e)i(r,t,{get:e[t],enumerable:!0})},h=(r,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of l(e))!c.call(r,n)&&n!==t&&i(r,n,{get:()=>e[n],enumerable:!(o=g(e,n))||o.enumerable});return r};var f=r=>h(i({},"__esModule",{value:!0}),r);var B={};w(B,{OBV2_WRAPPER_CONTEXT_URL:()=>a,openBadgeV2Plugin:()=>d});module.exports=f(B);var a="https://ctx.learncard.com/wrappers/obv2/1.0.0.json";var u="https://www.w3.org/ns/credentials/v2",O=s(r=>/^(https?:)?\/\//i.test(r)||r.startsWith("ipfs://")||r.startsWith("ipns://"),"isUrl"),V=s(r=>{try{let e=new URL(r);if(!["https:","http:","ipfs:","ipns:"].includes(e.protocol))throw new Error(`wrapOpenBadgeV2: Protocol '${e.protocol}' is not allowed. Only HTTP(S), IPFS, and IPNS are supported`);if(e.protocol==="ipfs:"||e.protocol==="ipns:")return;let o=e.hostname.toLowerCase();if(o==="localhost"||o==="127.0.0.1"||o==="0.0.0.0"||o==="::1")throw new Error("wrapOpenBadgeV2: Access to localhost is not allowed");if(o.startsWith("10.")||o.startsWith("192.168.")||o.startsWith("172.")&&/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(o))throw new Error("wrapOpenBadgeV2: Access to private IP ranges is not allowed");if(o.startsWith("169.254."))throw new Error("wrapOpenBadgeV2: Access to link-local addresses is not allowed")}catch(e){throw e instanceof TypeError?new Error("wrapOpenBadgeV2: Invalid URL format"):e}},"isValidAndSafeUrl");async function m(r){if(typeof r=="string"){let e=r.trim();if(!O(e)&&(e.startsWith("{")||e.startsWith("[")))try{return JSON.parse(e)}catch{throw new Error("wrapOpenBadgeV2: Provided string is not a valid URL or JSON")}V(e);try{let t=new AbortController,o=setTimeout(()=>t.abort(),1e4),n=await fetch(e,{signal:t.signal});if(clearTimeout(o),!n.ok)throw new Error(`wrapOpenBadgeV2: Failed to fetch OBv2 assertion: ${n.status}`);return await n.json()}catch(t){throw t instanceof TypeError?new Error(`wrapOpenBadgeV2: Network error while fetching OBv2 assertion: ${t.message}`):t?.name==="AbortError"?new Error("wrapOpenBadgeV2: Request timed out after 10 seconds"):t}}if(typeof r!="object"||r===null)throw new Error("wrapOpenBadgeV2: Input must be a valid object");return r}s(m,"getObv2Assertion");function y(r){if(!r||typeof r!="object")throw new Error("wrapOpenBadgeV2: Missing assertion");let e=r.id;if(typeof e!="string"||!e.length)throw new Error("wrapOpenBadgeV2: assertion.id is required");let t=r.type;if(!(typeof t=="string"&&t==="Assertion"||Array.isArray(t)&&t.includes("Assertion")))throw new Error("wrapOpenBadgeV2: assertion.type must include 'Assertion'");if(!r.issuedOn||typeof r.issuedOn!="string")throw new Error("wrapOpenBadgeV2: assertion.issuedOn (string) is required")}s(y,"validateObv2");var d=s(r=>({name:"OpenBadgeV2",displayName:"OpenBadge v2 Wrapper",description:"Wrap legacy OpenBadge v2.0 assertions into self-issued Verifiable Credentials",methods:{wrapOpenBadgeV2:s(async(e,t)=>{let o=r.id.did(),n=await m(t);y(n);let p={"@context":[u,a],id:`urn:uuid:${e.invoke.crypto().randomUUID()}`,type:["VerifiableCredential","LegacyOpenBadgeCredential"],issuer:o,validFrom:new Date().toISOString(),credentialSubject:{id:o},legacyAssertion:n};return r.invoke.issueCredential(p,{proofPurpose:"assertionMethod"})},"wrapOpenBadgeV2")}}),"openBadgeV2Plugin");
|
|
2
2
|
//# sourceMappingURL=open-badge-v2-plugin.cjs.production.min.js.map
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts", "../src/types.ts", "../src/plugin.ts"],
|
|
4
4
|
"sourcesContent": ["export { openBadgeV2Plugin } from './plugin';\nexport * from './types';\n", "import { Plugin } from '@learncard/core';\nimport { VC } from '@learncard/types';\nimport { VCPluginMethods } from '@learncard/vc-plugin';\nimport { CryptoPluginMethods } from '@learncard/crypto-plugin';\n\nexport type OpenBadgeV2PluginDependentMethods = VCPluginMethods & CryptoPluginMethods;\n\nexport type OpenBadgeV2PluginMethods = {\n /**\n * Wrap a legacy OpenBadge v2.0 assertion (object or URL) into a self-issued VC\n */\n wrapOpenBadgeV2: (obv2Assertion: object | string) => Promise<VC>;\n};\n\nexport type OpenBadgeV2Plugin = Plugin<\n 'OpenBadgeV2',\n any,\n OpenBadgeV2PluginMethods,\n 'id',\n OpenBadgeV2PluginDependentMethods\n>;\n\nexport const OBV2_WRAPPER_CONTEXT_URL = 'https://ctx.learncard.com/wrappers/obv2/1.0.0.json';\n", "import { LearnCard } from '@learncard/core';\nimport { UnsignedVC, VC } from '@learncard/types';\n\nimport {\n OpenBadgeV2Plugin,\n OBV2_WRAPPER_CONTEXT_URL,\n OpenBadgeV2PluginDependentMethods,\n} from './types';\n\nconst VC_V2_CONTEXT = 'https://www.w3.org/ns/credentials/v2';\n\nconst isUrl = (value: string): boolean =>\n /^(https?:)?\\/\\//i.test(value) || value.startsWith('ipfs://') || value.startsWith('ipns://');\n\nconst isValidAndSafeUrl = (url: string): void => {\n try {\n const parsedUrl = new URL(url);\n\n // Only allow http, https, ipfs, ipns protocols\n const allowedProtocols = ['https:', 'http:', 'ipfs:', 'ipns:'];\n if (!allowedProtocols.includes(parsedUrl.protocol)) {\n throw new Error(\n `wrapOpenBadgeV2: Protocol '${parsedUrl.protocol}' is not allowed. Only HTTP(S), IPFS, and IPNS are supported`\n );\n }\n\n // Skip internal network checks for IPFS/IPNS as they don't use traditional IP addresses\n if (parsedUrl.protocol === 'ipfs:' || parsedUrl.protocol === 'ipns:') {\n return;\n }\n\n // Block localhost and internal networks for HTTP(S)\n const hostname = parsedUrl.hostname.toLowerCase();\n\n // Block localhost variations\n if (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '0.0.0.0' ||\n hostname === '::1'\n ) {\n throw new Error('wrapOpenBadgeV2: Access to localhost is not allowed');\n }\n\n // Block private IP ranges (RFC 1918)\n if (\n hostname.startsWith('10.') ||\n hostname.startsWith('192.168.') ||\n (hostname.startsWith('172.') && /^172\\.(1[6-9]|2[0-9]|3[0-1])\\./.test(hostname))\n ) {\n throw new Error('wrapOpenBadgeV2: Access to private IP ranges is not allowed');\n }\n\n // Block link-local addresses\n if (hostname.startsWith('169.254.')) {\n throw new Error('wrapOpenBadgeV2: Access to link-local addresses is not allowed');\n }\n } catch (error) {\n if (error instanceof TypeError) {\n throw new Error('wrapOpenBadgeV2: Invalid URL format');\n }\n throw error;\n }\n};\n\nasync function getObv2Assertion(input: object | string): Promise<Record<string, any>> {\n if (typeof input === 'string') {\n // Treat string as URL. If it looks like raw JSON, try to parse for developer ergonomics\n const trimmed = input.trim();\n if (!isUrl(trimmed) && (trimmed.startsWith('{') || trimmed.startsWith('['))) {\n try {\n return JSON.parse(trimmed);\n } catch (e) {\n throw new Error('wrapOpenBadgeV2: Provided string is not a valid URL or JSON');\n }\n }\n\n // Validate URL for security\n isValidAndSafeUrl(trimmed);\n\n try {\n // Add timeout to prevent hanging requests (10 seconds)\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000);\n\n const res = await fetch(trimmed, {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!res.ok)\n throw new Error(`wrapOpenBadgeV2: Failed to fetch OBv2 assertion: ${res.status}`);\n\n return await res.json();\n } catch (error: any) {\n if (error instanceof TypeError) {\n throw new Error(\n `wrapOpenBadgeV2: Network error while fetching OBv2 assertion: ${error.message}`\n );\n }\n if (error?.name === 'AbortError') {\n throw new Error('wrapOpenBadgeV2: Request timed out after 10 seconds');\n }\n throw error;\n }\n }\n\n // Add basic type validation before assertion\n if (typeof input !== 'object' || input === null) {\n throw new Error('wrapOpenBadgeV2: Input must be a valid object');\n }\n\n return input as Record<string, any>;\n}\n\nfunction validateObv2(obv2: Record<string, any>): void {\n if (!obv2 || typeof obv2 !== 'object') throw new Error('wrapOpenBadgeV2: Missing assertion');\n\n const id = obv2.id;\n if (typeof id !== 'string' || !id.length)\n throw new Error('wrapOpenBadgeV2: assertion.id is required');\n\n const t = obv2.type;\n const hasAssertionType =\n (typeof t === 'string' && t === 'Assertion') ||\n (Array.isArray(t) && t.includes('Assertion'));\n if (!hasAssertionType)\n throw new Error(\"wrapOpenBadgeV2: assertion.type must include 'Assertion'\");\n\n if (!obv2.issuedOn || typeof obv2.issuedOn !== 'string') {\n throw new Error('wrapOpenBadgeV2: assertion.issuedOn (string) is required');\n }\n}\n\nexport const openBadgeV2Plugin = (\n learnCard: LearnCard<any, 'id', OpenBadgeV2PluginDependentMethods>\n): OpenBadgeV2Plugin => ({\n name: 'OpenBadgeV2',\n displayName: 'OpenBadge v2 Wrapper',\n description: 'Wrap legacy OpenBadge v2.0 assertions into self-issued Verifiable Credentials',\n methods: {\n wrapOpenBadgeV2: async (_learnCard, obv2Assertion: object | string): Promise<VC> => {\n const issuerDid = learnCard.id.did();\n\n const obv2 = await getObv2Assertion(obv2Assertion);\n validateObv2(obv2);\n\n const unsigned: UnsignedVC = {\n '@context': [VC_V2_CONTEXT, OBV2_WRAPPER_CONTEXT_URL],\n id: `urn:uuid:${_learnCard.invoke.crypto().randomUUID()}`,\n type: ['VerifiableCredential', 'LegacyOpenBadgeCredential'],\n issuer: issuerDid,\n validFrom: new Date().toISOString(),\n credentialSubject: {\n id: issuerDid,\n },\n legacyAssertion: obv2,\n };\n\n return learnCard.invoke.issueCredential(unsigned, { proofPurpose: 'assertionMethod' });\n },\n },\n});\n"],
|
|
5
|
-
"mappings": "4dAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,8BAAAE,EAAA,sBAAAC,IAAA,eAAAC,EAAAJ,GCsBO,IAAMK,EAA2B,qDCbxC,IAAMC,EAAgB,uCAEhBC,EAAQC,EAACC,GACX,mBAAmB,KAAKA,CAAK,GAAKA,EAAM,WAAW,SAAS,GAAKA,EAAM,WAAW,SAAS,EADjF,SAGRC,EAAoBF,EAACG,GAAsB,CAC7C,GAAI,CACA,IAAMC,EAAY,IAAI,IAAID,CAAG,EAI7B,GAAI,CADqB,CAAC,SAAU,QAAS,QAAS,OAAO,EACvC,SAASC,EAAU,QAAQ,EAC7C,MAAM,IAAI,MACN,8BAA8BA,EAAU,
|
|
6
|
-
"names": ["
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["index_exports", "__export", "OBV2_WRAPPER_CONTEXT_URL", "openBadgeV2Plugin", "__toCommonJS", "OBV2_WRAPPER_CONTEXT_URL", "VC_V2_CONTEXT", "isUrl", "__name", "value", "isValidAndSafeUrl", "url", "parsedUrl", "hostname", "error", "getObv2Assertion", "input", "trimmed", "controller", "timeoutId", "res", "validateObv2", "obv2", "id", "openBadgeV2Plugin", "learnCard", "_learnCard", "obv2Assertion", "issuerDid", "unsigned", "OBV2_WRAPPER_CONTEXT_URL"]
|
|
7
7
|
}
|
|
@@ -76,8 +76,7 @@ async function getObv2Assertion(input) {
|
|
|
76
76
|
}
|
|
77
77
|
__name(getObv2Assertion, "getObv2Assertion");
|
|
78
78
|
function validateObv2(obv2) {
|
|
79
|
-
if (!obv2 || typeof obv2 !== "object")
|
|
80
|
-
throw new Error("wrapOpenBadgeV2: Missing assertion");
|
|
79
|
+
if (!obv2 || typeof obv2 !== "object") throw new Error("wrapOpenBadgeV2: Missing assertion");
|
|
81
80
|
const id = obv2.id;
|
|
82
81
|
if (typeof id !== "string" || !id.length)
|
|
83
82
|
throw new Error("wrapOpenBadgeV2: assertion.id is required");
|
|
@@ -95,7 +94,7 @@ var openBadgeV2Plugin = /* @__PURE__ */ __name((learnCard) => ({
|
|
|
95
94
|
displayName: "OpenBadge v2 Wrapper",
|
|
96
95
|
description: "Wrap legacy OpenBadge v2.0 assertions into self-issued Verifiable Credentials",
|
|
97
96
|
methods: {
|
|
98
|
-
wrapOpenBadgeV2: async (_learnCard, obv2Assertion) => {
|
|
97
|
+
wrapOpenBadgeV2: /* @__PURE__ */ __name(async (_learnCard, obv2Assertion) => {
|
|
99
98
|
const issuerDid = learnCard.id.did();
|
|
100
99
|
const obv2 = await getObv2Assertion(obv2Assertion);
|
|
101
100
|
validateObv2(obv2);
|
|
@@ -104,14 +103,14 @@ var openBadgeV2Plugin = /* @__PURE__ */ __name((learnCard) => ({
|
|
|
104
103
|
id: `urn:uuid:${_learnCard.invoke.crypto().randomUUID()}`,
|
|
105
104
|
type: ["VerifiableCredential", "LegacyOpenBadgeCredential"],
|
|
106
105
|
issuer: issuerDid,
|
|
107
|
-
validFrom: new Date().toISOString(),
|
|
106
|
+
validFrom: (/* @__PURE__ */ new Date()).toISOString(),
|
|
108
107
|
credentialSubject: {
|
|
109
108
|
id: issuerDid
|
|
110
109
|
},
|
|
111
110
|
legacyAssertion: obv2
|
|
112
111
|
};
|
|
113
112
|
return learnCard.invoke.issueCredential(unsigned, { proofPurpose: "assertionMethod" });
|
|
114
|
-
}
|
|
113
|
+
}, "wrapOpenBadgeV2")
|
|
115
114
|
}
|
|
116
115
|
}), "openBadgeV2Plugin");
|
|
117
116
|
export {
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/types.ts", "../src/plugin.ts"],
|
|
4
4
|
"sourcesContent": ["import { Plugin } from '@learncard/core';\nimport { VC } from '@learncard/types';\nimport { VCPluginMethods } from '@learncard/vc-plugin';\nimport { CryptoPluginMethods } from '@learncard/crypto-plugin';\n\nexport type OpenBadgeV2PluginDependentMethods = VCPluginMethods & CryptoPluginMethods;\n\nexport type OpenBadgeV2PluginMethods = {\n /**\n * Wrap a legacy OpenBadge v2.0 assertion (object or URL) into a self-issued VC\n */\n wrapOpenBadgeV2: (obv2Assertion: object | string) => Promise<VC>;\n};\n\nexport type OpenBadgeV2Plugin = Plugin<\n 'OpenBadgeV2',\n any,\n OpenBadgeV2PluginMethods,\n 'id',\n OpenBadgeV2PluginDependentMethods\n>;\n\nexport const OBV2_WRAPPER_CONTEXT_URL = 'https://ctx.learncard.com/wrappers/obv2/1.0.0.json';\n", "import { LearnCard } from '@learncard/core';\nimport { UnsignedVC, VC } from '@learncard/types';\n\nimport {\n OpenBadgeV2Plugin,\n OBV2_WRAPPER_CONTEXT_URL,\n OpenBadgeV2PluginDependentMethods,\n} from './types';\n\nconst VC_V2_CONTEXT = 'https://www.w3.org/ns/credentials/v2';\n\nconst isUrl = (value: string): boolean =>\n /^(https?:)?\\/\\//i.test(value) || value.startsWith('ipfs://') || value.startsWith('ipns://');\n\nconst isValidAndSafeUrl = (url: string): void => {\n try {\n const parsedUrl = new URL(url);\n\n // Only allow http, https, ipfs, ipns protocols\n const allowedProtocols = ['https:', 'http:', 'ipfs:', 'ipns:'];\n if (!allowedProtocols.includes(parsedUrl.protocol)) {\n throw new Error(\n `wrapOpenBadgeV2: Protocol '${parsedUrl.protocol}' is not allowed. Only HTTP(S), IPFS, and IPNS are supported`\n );\n }\n\n // Skip internal network checks for IPFS/IPNS as they don't use traditional IP addresses\n if (parsedUrl.protocol === 'ipfs:' || parsedUrl.protocol === 'ipns:') {\n return;\n }\n\n // Block localhost and internal networks for HTTP(S)\n const hostname = parsedUrl.hostname.toLowerCase();\n\n // Block localhost variations\n if (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '0.0.0.0' ||\n hostname === '::1'\n ) {\n throw new Error('wrapOpenBadgeV2: Access to localhost is not allowed');\n }\n\n // Block private IP ranges (RFC 1918)\n if (\n hostname.startsWith('10.') ||\n hostname.startsWith('192.168.') ||\n (hostname.startsWith('172.') && /^172\\.(1[6-9]|2[0-9]|3[0-1])\\./.test(hostname))\n ) {\n throw new Error('wrapOpenBadgeV2: Access to private IP ranges is not allowed');\n }\n\n // Block link-local addresses\n if (hostname.startsWith('169.254.')) {\n throw new Error('wrapOpenBadgeV2: Access to link-local addresses is not allowed');\n }\n } catch (error) {\n if (error instanceof TypeError) {\n throw new Error('wrapOpenBadgeV2: Invalid URL format');\n }\n throw error;\n }\n};\n\nasync function getObv2Assertion(input: object | string): Promise<Record<string, any>> {\n if (typeof input === 'string') {\n // Treat string as URL. If it looks like raw JSON, try to parse for developer ergonomics\n const trimmed = input.trim();\n if (!isUrl(trimmed) && (trimmed.startsWith('{') || trimmed.startsWith('['))) {\n try {\n return JSON.parse(trimmed);\n } catch (e) {\n throw new Error('wrapOpenBadgeV2: Provided string is not a valid URL or JSON');\n }\n }\n\n // Validate URL for security\n isValidAndSafeUrl(trimmed);\n\n try {\n // Add timeout to prevent hanging requests (10 seconds)\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000);\n\n const res = await fetch(trimmed, {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!res.ok)\n throw new Error(`wrapOpenBadgeV2: Failed to fetch OBv2 assertion: ${res.status}`);\n\n return await res.json();\n } catch (error: any) {\n if (error instanceof TypeError) {\n throw new Error(\n `wrapOpenBadgeV2: Network error while fetching OBv2 assertion: ${error.message}`\n );\n }\n if (error?.name === 'AbortError') {\n throw new Error('wrapOpenBadgeV2: Request timed out after 10 seconds');\n }\n throw error;\n }\n }\n\n // Add basic type validation before assertion\n if (typeof input !== 'object' || input === null) {\n throw new Error('wrapOpenBadgeV2: Input must be a valid object');\n }\n\n return input as Record<string, any>;\n}\n\nfunction validateObv2(obv2: Record<string, any>): void {\n if (!obv2 || typeof obv2 !== 'object') throw new Error('wrapOpenBadgeV2: Missing assertion');\n\n const id = obv2.id;\n if (typeof id !== 'string' || !id.length)\n throw new Error('wrapOpenBadgeV2: assertion.id is required');\n\n const t = obv2.type;\n const hasAssertionType =\n (typeof t === 'string' && t === 'Assertion') ||\n (Array.isArray(t) && t.includes('Assertion'));\n if (!hasAssertionType)\n throw new Error(\"wrapOpenBadgeV2: assertion.type must include 'Assertion'\");\n\n if (!obv2.issuedOn || typeof obv2.issuedOn !== 'string') {\n throw new Error('wrapOpenBadgeV2: assertion.issuedOn (string) is required');\n }\n}\n\nexport const openBadgeV2Plugin = (\n learnCard: LearnCard<any, 'id', OpenBadgeV2PluginDependentMethods>\n): OpenBadgeV2Plugin => ({\n name: 'OpenBadgeV2',\n displayName: 'OpenBadge v2 Wrapper',\n description: 'Wrap legacy OpenBadge v2.0 assertions into self-issued Verifiable Credentials',\n methods: {\n wrapOpenBadgeV2: async (_learnCard, obv2Assertion: object | string): Promise<VC> => {\n const issuerDid = learnCard.id.did();\n\n const obv2 = await getObv2Assertion(obv2Assertion);\n validateObv2(obv2);\n\n const unsigned: UnsignedVC = {\n '@context': [VC_V2_CONTEXT, OBV2_WRAPPER_CONTEXT_URL],\n id: `urn:uuid:${_learnCard.invoke.crypto().randomUUID()}`,\n type: ['VerifiableCredential', 'LegacyOpenBadgeCredential'],\n issuer: issuerDid,\n validFrom: new Date().toISOString(),\n credentialSubject: {\n id: issuerDid,\n },\n legacyAssertion: obv2,\n };\n\n return learnCard.invoke.issueCredential(unsigned, { proofPurpose: 'assertionMethod' });\n },\n },\n});\n"],
|
|
5
|
-
"mappings": ";;;;AAsBO,IAAM,2BAA2B;;;ACbxC,IAAM,gBAAgB;AAEtB,IAAM,QAAQ,wBAAC,UACX,mBAAmB,KAAK,KAAK,KAAK,MAAM,WAAW,SAAS,KAAK,MAAM,WAAW,SAAS,GADjF;AAGd,IAAM,oBAAoB,wBAAC,QAAsB;AAC7C,MAAI;AACA,UAAM,YAAY,IAAI,IAAI,GAAG;AAG7B,UAAM,mBAAmB,CAAC,UAAU,SAAS,SAAS,OAAO;AAC7D,QAAI,CAAC,iBAAiB,SAAS,UAAU,QAAQ,GAAG;AAChD,YAAM,IAAI;AAAA,QACN,8BAA8B,UAAU;AAAA,
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@learncard/open-badge-v2-plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "OpenBadge v2 wrapper plugin for LearnCard. Wraps OBv2 assertions into self-issued VCs.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/open-badge-v2-plugin.esm.js",
|
|
@@ -9,19 +9,19 @@
|
|
|
9
9
|
],
|
|
10
10
|
"author": "Learning Economy Foundation (www.learningeconomy.io)",
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"homepage": "https://github.com/
|
|
12
|
+
"homepage": "https://github.com/learningeconomy/LearnCard/tree/main/packages/plugins/open-badge-v2/README.md",
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
|
-
"url": "
|
|
15
|
+
"url": "https://github.com/learningeconomy/LearnCard"
|
|
16
16
|
},
|
|
17
17
|
"bugs": {
|
|
18
|
-
"url": "https://github.com/
|
|
18
|
+
"url": "https://github.com/learningeconomy/LearnCard/issues"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/jest": "^29.2.2",
|
|
22
22
|
"@types/node": "^17.0.31",
|
|
23
23
|
"aqu": "0.4.3",
|
|
24
|
-
"esbuild": "^0.
|
|
24
|
+
"esbuild": "^0.27.1",
|
|
25
25
|
"esbuild-jest": "^0.5.0",
|
|
26
26
|
"esbuild-plugin-copy": "^1.3.0",
|
|
27
27
|
"jest": "^29.3.0",
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
},
|
|
31
31
|
"types": "./dist/index.d.ts",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@learncard/
|
|
34
|
-
"@learncard/
|
|
35
|
-
"@learncard/
|
|
36
|
-
"@learncard/
|
|
33
|
+
"@learncard/crypto-plugin": "1.1.2",
|
|
34
|
+
"@learncard/core": "9.4.2",
|
|
35
|
+
"@learncard/types": "5.11.1",
|
|
36
|
+
"@learncard/vc-plugin": "1.3.2"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "node ./scripts/build.mjs && shx cp ./scripts/mixedEntypoint.js ./dist/index.js && tsc --p tsconfig.json",
|