Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/vc-delegation-engine-examples.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Delegation Engine examples

on:
pull_request:
push:
branches:
- main
- master

jobs:
examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
persist-credentials: false

- uses: actions/setup-node@v4
with:
node-version: "22.x"

- run: yarn install --frozen-lockfile --ignore-scripts

- run: npm install -g turbo@2.0.5

- run: turbo telemetry disable

- name: Run example scripts (pharmacy, delegation, staff, ai-agent, …)
run: turbo run examples --filter @docknetwork/vc-delegation-engine
84 changes: 77 additions & 7 deletions examples/src/delegation.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
verifyPresentation,
documentLoader as credentialDocumentLoader,
} from '@docknetwork/credential-sdk/vc';
import { MAY_CLAIM_IRI } from '@docknetwork/vc-delegation-engine';
import { MAY_CLAIM_IRI, computePolicyDigestHex } from '@docknetwork/vc-delegation-engine';
import * as cedar from '@cedar-policy/cedar-wasm/nodejs';

const cedarPolicies = {
Expand All @@ -24,6 +24,7 @@ permit(
const AUTHORITY_DID = 'did:example:authority';
const DELEGATE_DID = 'did:example:delegator';
const SUBJECT_DID = 'did:example:alice';
const POLICY_ID = 'urn:uuid:4f4f0f7b-4c55-4c88-bc44-43f2e7eb2f10';
const CHALLENGE = 'credit-score-demo';
const DOMAIN = 'delegation.example';
const W3CONTEXT = 'https://www.w3.org/2018/credentials/v1';
Expand Down Expand Up @@ -61,6 +62,53 @@ const CREDIT_SCORE_CONTEXT = [
},
];

const delegationPolicy = {
id: POLICY_ID,
ruleset: {
capabilities: [
{
name: 'profile',
schema: {
type: 'object',
},
},
{
name: 'status',
schema: {
type: 'string',
enum: ['active', 'inactive'],
},
},
],
roles: [
{
roleId: 'delegator',
parentRoleId: null,
attributes: ['*'],
capabilityGrants: [
{
capability: 'profile',
schema: {
type: 'object',
},
},
{
capability: 'status',
schema: {
type: 'string',
enum: ['active', 'inactive'],
},
},
],
},
],
overallConstraints: {
maxDelegationDepth: 1,
},
},
};
const delegationPolicyDigest = computePolicyDigestHex(delegationPolicy);

const BASE_JWK_KEY = {
type: 'JsonWebKey2020',
publicKeyJwk: {
Expand Down Expand Up @@ -139,6 +187,9 @@ async function exampleDocumentLoader(url) {
if (url === DELEGATE_KEY.id) {
return { contextUrl: null, documentUrl: url, document: DELEGATE_KEY };
}
if (url === POLICY_ID) {
return { contextUrl: null, documentUrl: url, document: delegationPolicy };
}
return baseLoader(url);
}

Expand All @@ -161,14 +212,25 @@ async function main() {
},
},
},
[MAY_CLAIM_IRI]: ['$.scope.profile.financial.creditScore', 'status'],
[MAY_CLAIM_IRI]: [
'$.profile',
'$.profile.financial',
'$.profile.financial.creditScore',
'status',
],
profile: {
financial: {
creditScore: 800,
},
},
status: 'active',
body: 'Issuer of Credit Scores',
},
rootCredentialId: rootId,
previousCredentialId: null,
delegationPolicyId: 'urn:uuid:4f4f0f7b-4c55-4c88-bc44-43f2e7eb2f10',
delegationPolicyDigest:
'3f2d2d6f2d7b6e0e9b0cfd5b6ac1e8f5f31d2d41e8d39d6b8d36b1d4c3a8d72a',
delegationPolicyId: POLICY_ID,
delegationPolicyDigest,
delegationRoleId: 'delegator',
});

const creditScoreCredential = await issueCredential(DELEGATE_KEY, {
Expand All @@ -181,13 +243,16 @@ async function main() {
id: SUBJECT_DID,
profile: {
financial: {
creditScore: 760,
creditScore: 800,
},
},
status: 'active',
},
rootCredentialId: rootId,
previousCredentialId: rootId,
delegationPolicyId: POLICY_ID,
delegationPolicyDigest,
delegationRoleId: 'delegator',
});

const signedPresentation = await signPresentation(
Expand Down Expand Up @@ -217,9 +282,14 @@ async function main() {
console.log('Credential verifications:', verification.credentialResults.map((r) => r.verified));

if (verification.delegationResult) {
const { decision, summaries, authorizations } = verification.delegationResult;
const {
decision, summaries, authorizations, failures,
} = verification.delegationResult;
console.log('Delegation decision:', decision);
console.log('Authorized claims derived from chain:', summaries?.[0]?.authorizedClaims);
if (decision !== 'allow') {
console.log('Delegation failures:', failures);
}
if (authorizations?.length) {
console.log('Cedar authorization decisions:', authorizations);
}
Expand Down
13 changes: 13 additions & 0 deletions packages/credential-sdk/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"parser": "@babel/eslint-parser",
"parserOptions": {
"requireConfigFile": false,
"babelOptions": {
"babelrc": false,
"configFile": false,
"plugins": ["@babel/plugin-syntax-import-attributes"],
"presets": ["@babel/preset-env"],
"sourceType": "module"
}
}
}
11 changes: 6 additions & 5 deletions packages/credential-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"devDependencies": {
"@babel/cli": "^7.24.1",
"@babel/core": "^7.24.3",
"@babel/eslint-parser": "^7.25.9",
"@babel/node": "^7.23.9",
"@babel/plugin-syntax-import-attributes": "^7.25.6",
"@babel/plugin-transform-modules-commonjs": "^7.24.1",
Expand All @@ -77,9 +78,9 @@
"@helia/strings": "^3.0.5",
"@rollup/plugin-alias": "^4.0.2",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^24.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.3.1",
"@rollup/plugin-wasm": "^5.1.0",
"@types/jest": "^29.5.12",
"babel-eslint": "^10.1.0",
Expand All @@ -95,9 +96,9 @@
"jest-environment-node": "^29.7.0",
"jsdoc": "^3.6.3",
"jsdoc-typeof-plugin": "^1.0.0",
"rollup": "2.79.2",
"rollup": "^4.28.0",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-multi-input": "^1.3.2",
"rollup-plugin-multi-input": "^1.4.1",
"rollup-plugin-node-polyfills": "^0.2.1",
"rollup-plugin-terser": "^7.0.2"
},
Expand Down
93 changes: 58 additions & 35 deletions packages/credential-sdk/rollup.config.mjs
Original file line number Diff line number Diff line change
@@ -1,37 +1,60 @@
import json from "@rollup/plugin-json";
import multiInput from "rollup-plugin-multi-input";
import commonjs from "@rollup/plugin-commonjs";
import path from 'node:path';
import { fileURLToPath } from 'node:url';

export default async function () {
return [
{
plugins: [
multiInput(),
json(),
// terser(),
commonjs(),
// Temporarily disabled, not sure if required
// since rify is a node module doesnt seem to work
// but would be nice to try embed it
// wasm({
// sync: ['*.wasm'],
// }),
],
input: ["src/**/*.js"],
output: [
{
sourcemap: true,
dir: "dist/esm",
format: "esm",
entryFileNames: "[name].js",
},
{
sourcemap: true,
dir: "dist/cjs",
format: "cjs",
entryFileNames: "[name].cjs",
},
],
},
];
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import multiInput from 'rollup-plugin-multi-input';

const sep = path.sep;
const packageRoot = path.normalize(path.dirname(fileURLToPath(import.meta.url)));

/**
* Rollup 4 follows deps into .wasm etc.; keep real dependencies external.
* Yarn workspaces may symlink this package under node_modules — still bundle files under this package root.
*/
function isExternal(id) {
if (id.startsWith('\0')) {
return false;
}
// Relative paths and multi-input entries like "src/crypto/index.js" (no leading ./)
if (id.startsWith('.') || id.startsWith(`src${sep}`) || id.startsWith('src/')) {
return false;
}
if (path.isAbsolute(id)) {
const normalized = path.normalize(id);
if (normalized.startsWith(packageRoot)) {
return false;
}
return id.includes(`${sep}node_modules${sep}`);
}
return true;
}

export default [
{
input: ['src/**/*.js'],
external: isExternal,
plugins: [
multiInput(),
nodeResolve({ preferBuiltins: true }),
json({ preferConst: true }),
commonjs(),
],
output: [
{
sourcemap: true,
dir: 'dist/esm',
format: 'esm',
entryFileNames: '[name].js',
},
{
sourcemap: true,
dir: 'dist/cjs',
format: 'cjs',
entryFileNames: '[name].cjs',
interop: 'auto',
},
],
},
];
42 changes: 21 additions & 21 deletions packages/credential-sdk/src/vc/contexts.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import vcExamplesV1 from './contexts/vc-examples-v1';
import ed25519V1Context from './contexts/ed25519-2020-v1-context.json';
import sr25519Context from './contexts/sr25519-context.json';
import secContext from './contexts/security_context';
import secContextV1 from './contexts/security-v1.json';
import didV1Context from './contexts/did-v1-updated.json';
import credV1Context from './contexts/credential-v1-updated.json';
import schema from './contexts/schema.json';
import odrl from './contexts/odrl.json';
import bbsV1Context from './contexts/bbs-v1.json';
import dockBBSV1Context from './contexts/dock-bbs-v1.json';
import dockBBS23V1Context from './contexts/dock-bbs23-v1.json';
import dockPSV1Context from './contexts/dock-ps-v1.json';
import dockBBDT16V1Context from './contexts/dock-bbdt16-v1.json';
import dockPrettyVCContext from './contexts/prettyvc.json';
import jws2020V1Context from './contexts/jws-2020-v1.json';
import delegationCredentialContext from './contexts/delegation-credential.json';
import statusList21Context from './contexts/status-list-21';
import privateStatusList21Context from './contexts/private-status-list-21';
import sphereonId from './contexts/sphereon-wallet-identity-v1.json';
import citizenshipContext from './contexts/citizen-v1.json';
import vcExamplesV1 from './contexts/vc-examples-v1.js';
import ed25519V1Context from './contexts/ed25519-2020-v1-context.json' with { type: 'json' };
import sr25519Context from './contexts/sr25519-context.json' with { type: 'json' };
import secContext from './contexts/security_context.js';
import secContextV1 from './contexts/security-v1.json' with { type: 'json' };
import didV1Context from './contexts/did-v1-updated.json' with { type: 'json' };
import credV1Context from './contexts/credential-v1-updated.json' with { type: 'json' };
import schema from './contexts/schema.json' with { type: 'json' };
import odrl from './contexts/odrl.json' with { type: 'json' };
import bbsV1Context from './contexts/bbs-v1.json' with { type: 'json' };
import dockBBSV1Context from './contexts/dock-bbs-v1.json' with { type: 'json' };
import dockBBS23V1Context from './contexts/dock-bbs23-v1.json' with { type: 'json' };
import dockPSV1Context from './contexts/dock-ps-v1.json' with { type: 'json' };
import dockBBDT16V1Context from './contexts/dock-bbdt16-v1.json' with { type: 'json' };
import dockPrettyVCContext from './contexts/prettyvc.json' with { type: 'json' };
import jws2020V1Context from './contexts/jws-2020-v1.json' with { type: 'json' };
import delegationCredentialContext from './contexts/delegation-credential.json' with { type: 'json' };
import statusList21Context from './contexts/status-list-21.js';
import privateStatusList21Context from './contexts/private-status-list-21.js';
import sphereonId from './contexts/sphereon-wallet-identity-v1.json' with { type: 'json' };
import citizenshipContext from './contexts/citizen-v1.json' with { type: 'json' };

// Lookup of following URLs will lead to loading data from the context directory, this is done as the Sr25519 keys are not
// supported in any W3C standard and vc-js has them stored locally. This is a temporary solution.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
"delegationPolicyDigest": {
"@id": "dockDelegation:delegationPolicyDigest"
},
"delegationRoleId": {
"@id": "dockDelegation:delegationRoleId"
},
"mayClaim": {
"@id": "dockalpha:mayClaim",
"@container": "@set"
Expand Down
Loading
Loading