├── .adr-dir ├── .dockerignore ├── .gitattributes ├── .github └── workflows │ ├── container-image-publish.yml │ ├── docs.yml │ └── main.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.js ├── CODE_OF_CONDUCT.md ├── EG.jpg ├── EW.png ├── IM.png ├── LICENSE ├── MAINTAINERS ├── README.md ├── apps └── vc-api │ ├── .env.tutorial │ ├── .eslintrc.js │ ├── .gitignore │ ├── CODE_OF_CONDUCT.md │ ├── Dockerfile │ ├── Exhibit B -- Linux Foundation Europe Policies.pdf │ ├── README.md │ ├── docs │ ├── api-versioning.md │ ├── architecture │ │ └── decisions │ │ │ ├── 0001-record-architecture-decisions.md │ │ │ ├── 0002-use-credo-for-key-and-credential-operations.md │ │ │ └── 0003-use-sqlite-for-credo-db.md │ ├── exchanges │ │ ├── README.md │ │ ├── example-exchange-definitions │ │ │ ├── README.md │ │ │ ├── issuance-exchange.md │ │ │ ├── issuance-self-sign-exchange.md │ │ │ ├── multi-credential-exchange-definition-with-or-condition.md │ │ │ ├── presentation-exchange.md │ │ │ ├── presentation-issuance-exchange.md │ │ │ ├── presentation-issuance-self-sign-exchange.md │ │ │ ├── presentation-self-signed-exchange.md │ │ │ └── self-signed-exchange.md │ │ └── exchange-tutorials │ │ │ ├── credential-exchange-multiple-credential-tutorial.md │ │ │ ├── credential-exchange-self-signed-credential-tutorial.md │ │ │ └── credential-exchange-single-credential-tutorial.md │ ├── key-export-import-tutorial.md │ ├── openapi.json │ ├── vc-api.postman_environment.json │ └── workflows │ │ ├── README.md │ │ ├── example-workflow-definitions │ │ ├── README.md │ │ ├── issuance-self-sign-workflow.md │ │ ├── issuance-workflow.md │ │ ├── multi-credential-workflow-with-or-condition.md │ │ ├── presentation-exchange.md │ │ ├── presentation-issuance-self-sign-workflow.md │ │ ├── presentation-issuance-workflow.md │ │ ├── presentation-self-signed-workflow.md │ │ └── self-signed-workflow.md │ │ └── workflow-tutorials │ │ ├── self-signed-credential-workflow-tutorial.md │ │ └── single-credential-workflow-tutorial.md │ ├── nest-cli.json │ ├── package.json │ ├── scripts │ └── write-open-api-json.ts │ ├── src │ ├── app.module.ts │ ├── config │ │ └── env-vars-validation-schema.ts │ ├── credo │ │ ├── __mocks__ │ │ │ └── credo.service.ts │ │ ├── credo.module.ts │ │ └── credo.service.ts │ ├── db-config.ts │ ├── did │ │ ├── did.controller.spec.ts │ │ ├── did.controller.ts │ │ ├── did.module.ts │ │ ├── did.service.spec.ts │ │ ├── did.service.ts │ │ ├── dto │ │ │ ├── create-did-options.dto.ts │ │ │ ├── create-did-response.dto.ts │ │ │ ├── json-web-key.dto.ts │ │ │ └── verification-method.dto.ts │ │ ├── entities │ │ │ ├── did-document.entity.ts │ │ │ └── verification-method.entity.ts │ │ └── types │ │ │ └── did-method.ts │ ├── dtos │ │ ├── bad-request-error-response.dto.ts │ │ ├── conflict-error-response.dto.ts │ │ ├── internal-server-error-response.dto.ts │ │ └── not-found-error-response.dto.ts │ ├── exception-filters │ │ └── transaction-entity-exception.filter.ts │ ├── index.ts │ ├── key │ │ ├── dtos │ │ │ ├── key-description.dto.ts │ │ │ └── key-pair.dto.ts │ │ ├── key-pair.entity.ts │ │ ├── key-types.ts │ │ ├── key.controller.spec.ts │ │ ├── key.controller.ts │ │ ├── key.module.ts │ │ ├── key.service.spec.ts │ │ └── key.service.ts │ ├── main.ts │ ├── middlewares │ │ ├── http-logger.middleware.ts │ │ └── index.ts │ ├── seeder │ │ ├── fixtures │ │ │ └── key-pair.fixture.ts │ │ ├── seeder.module.ts │ │ ├── seeder.service.spec.ts │ │ └── seeder.service.ts │ ├── setup.ts │ ├── utils │ │ └── crypto.utils.ts │ └── vc-api │ │ ├── credentials │ │ ├── credentials.service.spec.ts │ │ ├── credentials.service.ts │ │ ├── dtos │ │ │ ├── authenticate.dto.ts │ │ │ ├── credential.dto.ts │ │ │ ├── custom-class-validator │ │ │ │ └── is-string-or-string-array.ts │ │ │ ├── issue-credential.dto.ts │ │ │ ├── issue-options.dto.ts │ │ │ ├── issuer.dto.ts │ │ │ ├── presentation.dto.ts │ │ │ ├── prove-presentation-options.dto.ts │ │ │ ├── prove-presentation.dto.ts │ │ │ ├── verifiable-credential.dto.ts │ │ │ ├── verifiable-presentation.dto.ts │ │ │ ├── verification-result.dto.ts │ │ │ ├── verify-credential.dto.ts │ │ │ ├── verify-options.dto.ts │ │ │ └── verify-presentation.dto.ts │ │ ├── types │ │ │ ├── credential-verifier.ts │ │ │ ├── presentation-verifier.ts │ │ │ ├── problem-detail.ts │ │ │ ├── verification-result.ts │ │ │ └── verify-options.ts │ │ └── utils │ │ │ └── verification-result-transformer.ts │ │ ├── exchanges │ │ ├── dtos │ │ │ ├── callback-configuration.dto.spec.ts │ │ │ ├── callback-configuration.dto.ts │ │ │ ├── callback.dto.ts │ │ │ ├── custom-validators │ │ │ │ ├── index.ts │ │ │ │ ├── issuer.validator.ts │ │ │ │ └── presentation-definition-credential-query.validator.ts │ │ │ ├── exchange-definition.dto.spec.ts │ │ │ ├── exchange-definition.dto.ts │ │ │ ├── exchange-interact-service-definition.dto.ts │ │ │ ├── exchange-response.dto.ts │ │ │ ├── presentation-definition.dto.ts │ │ │ ├── presentation-submission-full.dto.ts │ │ │ ├── presentation-submission-secure.dto.ts │ │ │ ├── submission-review.dto.ts │ │ │ ├── transaction.dto.ts │ │ │ ├── vp-request-did-auth-query.dto.ts │ │ │ ├── vp-request-interact-service.dto.spec.ts │ │ │ ├── vp-request-interact-service.dto.ts │ │ │ ├── vp-request-interact.dto.ts │ │ │ ├── vp-request-presentation-defintion-query.dto.spec.ts │ │ │ ├── vp-request-presentation-defintion-query.dto.ts │ │ │ ├── vp-request-query.dto.ts │ │ │ └── vp-request.dto.ts │ │ ├── entities │ │ │ ├── exchange.entity.spec.ts │ │ │ ├── exchange.entity.ts │ │ │ ├── presentation-review.entity.ts │ │ │ ├── presentation-submission.entity.ts │ │ │ ├── transaction.entity.spec.ts │ │ │ ├── transaction.entity.ts │ │ │ └── vp-request.entity.ts │ │ ├── exchange.service.spec.ts │ │ ├── exchange.service.ts │ │ ├── types │ │ │ ├── callback-configuration.ts │ │ │ ├── credential.ts │ │ │ ├── presentation-review-status.ts │ │ │ ├── presentation.ts │ │ │ ├── submission-verifier.ts │ │ │ ├── verifiable-credential.ts │ │ │ ├── verifiable-presentation.ts │ │ │ ├── vp-request-interact-service-definition.ts │ │ │ ├── vp-request-interact-service-type.ts │ │ │ ├── vp-request-interact-service.ts │ │ │ ├── vp-request-query-type.ts │ │ │ └── vp-request-query.ts │ │ ├── vp-submission-verifier.service.spec.ts │ │ └── vp-submission-verifier.service.ts │ │ ├── exhaustiveMatchGuard.ts │ │ ├── vc-api.controller.spec.ts │ │ ├── vc-api.controller.ts │ │ ├── vc-api.module.ts │ │ └── workflows │ │ ├── dtos │ │ ├── callback-configuration.dto.ts │ │ ├── callback.dto.ts │ │ ├── create-exchange-success.dto.ts │ │ ├── create-exchange.dto.ts │ │ ├── create-workflow-request.dto.ts │ │ ├── create-workflow-success.dto.ts │ │ ├── custom-validators │ │ │ ├── index.ts │ │ │ ├── issuer.validator.ts │ │ │ └── presentation-definition-credential-query.validator.ts │ │ ├── exchange-interact-service-definition.dto.ts │ │ ├── exchange-response.dto.ts │ │ ├── exchange-state.dto.ts │ │ ├── exchange-step-state.dto.ts │ │ ├── participate-in-exchange.dto.ts │ │ ├── presentation-definition.dto.ts │ │ ├── presentation-submission-full.dto.ts │ │ ├── submission-review.dto.ts │ │ ├── vp-request-definition.dto.ts │ │ ├── vp-request-did-auth-query.dto.ts │ │ ├── vp-request-presentation-defintion-query.dto.ts │ │ ├── vp-request-query.dto.ts │ │ ├── vp-request.dto.ts │ │ ├── workflow-step-definition.dto.ts │ │ └── workflow-step.dto.ts │ │ ├── entities │ │ ├── wf-exchange.entity.ts │ │ └── workflow.entity.ts │ │ ├── types │ │ ├── callback-configuration.ts │ │ ├── credential.ts │ │ ├── exchange-status.ts │ │ ├── exchange-step.ts │ │ ├── issuance-exchange-step.ts │ │ ├── presentation-submission.ts │ │ ├── presentation.ts │ │ ├── query-exchange-step.ts │ │ ├── submission-verifier.ts │ │ ├── verifiable-credential.ts │ │ ├── verifiable-presentation.ts │ │ ├── vp-request-interact-service-type.ts │ │ ├── vp-request-query-type.ts │ │ └── vp-request-query.ts │ │ ├── vp-submission-verifier.service.ts │ │ └── workflow.service.ts │ ├── static-assets │ └── .well-known │ │ └── assetlinks.json │ ├── test │ ├── app.e2e-spec.ts │ ├── did │ │ ├── did.e2e-suite.ts │ │ └── did.service.spec.data.ts │ ├── jest-e2e.json │ ├── key │ │ ├── key.e2e-suite.ts │ │ └── key.service.spec.data.ts │ ├── vc-api │ │ ├── credential.service.spec.data.ts │ │ ├── credential.service.spec.key.ts │ │ ├── credentials │ │ │ └── vc-api.e2e-suite.ts │ │ ├── exchanges │ │ │ ├── consent-and-resident-card-credentialexchange │ │ │ │ ├── consent-and-resident-card-credential-exchange.ts │ │ │ │ ├── consent-and-resident-card-presentation.exchange.ts │ │ │ │ └── consent-and-resident-card.e2e-suite.ts │ │ │ ├── rebeam │ │ │ │ ├── rebeam-cpo-node.ts │ │ │ │ ├── rebeam-supplier.ts │ │ │ │ └── rebeam.e2e-suite.ts │ │ │ └── resident-card │ │ │ │ ├── resident-card-issuance.exchange.ts │ │ │ │ ├── resident-card-presentation.exchange.ts │ │ │ │ └── resident-card.e2e-suite.ts │ │ └── workflows │ │ │ └── resident-card │ │ │ ├── resident-card-issuance.workflow.ts │ │ │ ├── resident-card-presentation.workflow.ts │ │ │ └── resident-card.e2e-suite.ts │ └── wallet-client.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── common ├── autoinstallers │ └── rush-prettier │ │ ├── package.json │ │ └── pnpm-lock.yaml ├── config │ └── rush │ │ ├── .npmrc │ │ ├── .npmrc-publish │ │ ├── .pnpmfile.cjs │ │ ├── artifactory.json │ │ ├── build-cache.json │ │ ├── command-line.json │ │ ├── common-versions.json │ │ ├── deploy-ssi-vc-api.json │ │ ├── experiments.json │ │ ├── pnpm-config.json │ │ ├── pnpm-lock.yaml │ │ ├── repo-state.json │ │ └── version-policies.json ├── git-hooks │ ├── commit-msg.sample │ └── pre-commit ├── pnpm-patches │ └── @credo-ts__core@0.5.10.patch └── scripts │ ├── install-run-rush-pnpm.js │ ├── install-run-rush.js │ ├── install-run-rushx.js │ └── install-run.js ├── contributing.md ├── docs ├── image-src.txt ├── img │ └── VC API Components - OWF Copy.svg └── index.md ├── index.md ├── jest.config.js ├── libraries ├── did │ ├── .eslintignore │ ├── .eslintrc.js │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── did-ethr-factory.spec.ts │ │ ├── did-ethr-factory.ts │ │ ├── did-key-factory.spec.ts │ │ ├── did-key-factory.ts │ │ ├── index.ts │ │ └── json-web-key.ts │ └── tsconfig.json └── webkms │ ├── .eslintignore │ ├── .eslintrc.js │ ├── package.json │ ├── src │ ├── generate-key.ts │ ├── index.ts │ └── key-description.ts │ └── tsconfig.json ├── license-header.txt ├── mkdocs.yml ├── rush.json ├── tests └── e2e │ ├── .eslintignore │ ├── .eslintrc.js │ ├── package.json │ ├── src │ └── main-suite.e2e-spec.ts │ └── tsconfig.json └── tsconfig.json /.adr-dir: -------------------------------------------------------------------------------- 1 | apps/vc-api/docs/architecture/decisions 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | /Dockerfile 2 | **/Dockerfile 3 | docker-compose.yml 4 | .dockerignore 5 | **/node_modules 6 | npm-debug.log 7 | .vscode 8 | .next 9 | .git 10 | .env 11 | **/dist 12 | # Rush temporary files 13 | common/deploy/ 14 | common/temp/ 15 | common/autoinstallers/*/.npmrc 16 | **/.rush/temp/ 17 | 18 | **/*.log 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Don't allow people to merge changes to these generated files, because the result 2 | # may be invalid. You need to run "rush update" again. 3 | pnpm-lock.yaml merge=binary 4 | shrinkwrap.yaml merge=binary 5 | npm-shrinkwrap.json merge=binary 6 | yarn.lock merge=binary 7 | 8 | # Rush's JSON config files use JavaScript-style code comments. The rule below prevents pedantic 9 | # syntax highlighters such as GitHub's from highlighting these comments as errors. Your text editor 10 | # may also require a special configuration to allow comments in JSON. 11 | # 12 | # For more information, see this issue: https://github.com/microsoft/rushstack/issues/1088 13 | # 14 | *.json linguist-language=JSON-with-Comments 15 | -------------------------------------------------------------------------------- /.github/workflows/container-image-publish.yml: -------------------------------------------------------------------------------- 1 | name: Build and Publish Docker Image 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: 7 | - created 8 | 9 | jobs: 10 | build-and-push: 11 | runs-on: ubuntu-latest 12 | 13 | permissions: 14 | contents: read 15 | packages: write 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Get latest release version 22 | id: get_version 23 | run: | 24 | LATEST_VERSION=$(gh release list --limit 1 --json tagName --jq '.[0].tagName') 25 | echo "LATEST_VERSION=${LATEST_VERSION}" >> $GITHUB_ENV 26 | env: 27 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | 29 | - name: Check if package exists 30 | id: check_package 31 | run: | 32 | if docker manifest inspect ghcr.io/${{ github.repository }}:${{ env.LATEST_VERSION }} > /dev/null 2>&1; then 33 | echo "Package already exists. Aborting." 34 | exit 1 35 | fi 36 | 37 | - name: Log in to GitHub Container Registry 38 | run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin 39 | 40 | - name: Build Docker image 41 | run: | 42 | docker build -t ghcr.io/${{ github.repository }}:${{ env.LATEST_VERSION }} -f apps/vc-api/Dockerfile . 43 | 44 | - name: Push Docker image to GitHub Packages 45 | run: | 46 | docker push ghcr.io/${{ github.repository }}:${{ env.LATEST_VERSION }} 47 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs-site 2 | on: 3 | push: 4 | branches: 5 | - develop 6 | permissions: 7 | contents: write 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: Configure Git Credentials 14 | run: | 15 | git config user.name github-actions[bot] 16 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 17 | - uses: actions/setup-python@v5 18 | with: 19 | python-version: 3.x 20 | - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV 21 | - uses: actions/cache@v4 22 | with: 23 | key: mkdocs-material-${{ env.cache_id }} 24 | path: .cache 25 | restore-keys: | 26 | mkdocs-material- 27 | - run: pip install mkdocs-material 28 | - run: mkdocs gh-deploy --force 29 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | build-and-test: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | with: 12 | fetch-depth: 0 13 | 14 | - uses: actions/setup-node@v2 15 | with: 16 | node-version: '18.12' 17 | 18 | - name: Cache Rush 19 | uses: actions/cache@v2 20 | with: 21 | path: | 22 | common/temp/install-run 23 | ~/.rush 24 | key: ${{ runner.os }}-${{ hashFiles('rush.json') }} 25 | 26 | - name: Cache pnpm 27 | uses: actions/cache@v2 28 | with: 29 | path: | 30 | common/temp/pnpm-store 31 | key: ${{ runner.os }}-${{ hashFiles('common/config/rush/pnpm-lock.yaml') }} 32 | 33 | - name: Rush install 34 | run: node common/scripts/install-run-rush.js install 35 | 36 | - name: Rush build 37 | run: node common/scripts/install-run-rush.js build --verbose 38 | 39 | - name: Run test 40 | run: node common/scripts/install-run-rush.js test --verbose 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | *.log 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # Runtime data 8 | *.pid 9 | *.seed 10 | *.pid.lock 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # Bower dependency directory (https://bower.io/) 25 | bower_components 26 | 27 | # node-waf configuration 28 | .lock-wscript 29 | 30 | # Compiled binary addons (https://nodejs.org/api/addons.html) 31 | build/Release 32 | 33 | # Dependency directories 34 | node_modules/ 35 | jspm_packages/ 36 | 37 | # Optional npm cache directory 38 | .npm 39 | 40 | # Optional eslint cache 41 | .eslintcache 42 | 43 | # Optional REPL history 44 | .node_repl_history 45 | 46 | # Output of 'npm pack' 47 | *.tgz 48 | 49 | # Yarn Integrity file 50 | .yarn-integrity 51 | 52 | # dotenv environment variables file 53 | .env 54 | 55 | # next.js build output 56 | .next 57 | 58 | # OS X temporary files 59 | .DS_Store 60 | 61 | # Rush temporary files 62 | common/deploy/ 63 | common/temp/ 64 | common/autoinstallers/*/.npmrc 65 | **/.rush/temp/ 66 | 67 | # Heft 68 | .heft 69 | 70 | # Custom from default rush file 71 | dist 72 | .vscode/ 73 | docker-compose.yml 74 | .aider* 75 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------------------------------------------- 2 | # Keep this section in sync with .gitignore 3 | #------------------------------------------------------------------------------------------------------------------- 4 | 5 | 👋 (copy + paste your .gitignore file contents here) 👋 6 | 7 | #------------------------------------------------------------------------------------------------------------------- 8 | # Prettier-specific overrides 9 | #------------------------------------------------------------------------------------------------------------------- 10 | 11 | # Rush files 12 | common/changes/ 13 | common/scripts/ 14 | common/config/ 15 | common/temp/ 16 | CHANGELOG.* 17 | 18 | # Package manager files 19 | pnpm-lock.yaml 20 | yarn.lock 21 | package-lock.json 22 | shrinkwrap.json 23 | 24 | # Build outputs 25 | dist 26 | lib 27 | devops/ 28 | # Prettier reformats code blocks inside Markdown, which affects rendered output 29 | *.md 30 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | // Documentation for this file: https://prettier.io/docs/en/configuration.html 2 | module.exports = { 3 | ...require('./common/temp/node_modules/@energyweb/prettier-config'), 4 | // We use a larger print width because Prettier's word-wrapping seems to be tuned 5 | // for plain JavaScript without type annotations 6 | printWidth: 110, 7 | 8 | // Use .gitattributes to manage newlines 9 | endOfLine: 'auto', 10 | 11 | // For ES5, trailing commas cannot be used in function parameters; it is counterintuitive 12 | // to use them for arrays only 13 | trailingComma: 'none' 14 | }; 15 | -------------------------------------------------------------------------------- /EG.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwallet-foundation-labs/vc-api/d8cd9adfcc9a30e73682c9728b053f569575ebcc/EG.jpg -------------------------------------------------------------------------------- /EW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwallet-foundation-labs/vc-api/d8cd9adfcc9a30e73682c9728b053f569575ebcc/EW.png -------------------------------------------------------------------------------- /IM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwallet-foundation-labs/vc-api/d8cd9adfcc9a30e73682c9728b053f569575ebcc/IM.png -------------------------------------------------------------------------------- /MAINTAINERS: -------------------------------------------------------------------------------- 1 | John Henderson 2 | Nitin Gavhane 3 | Shikhar Bhatt 4 | Sachin Mesare -------------------------------------------------------------------------------- /apps/vc-api/.env.tutorial: -------------------------------------------------------------------------------- 1 | ISSUER_URL=http://localhost:3000 -------------------------------------------------------------------------------- /apps/vc-api/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | module.exports = { 7 | parserOptions: { 8 | tsconfigRootDir: __dirname, 9 | project: 'tsconfig.json' 10 | }, 11 | extends: ['@energyweb'], 12 | env: { 13 | es2021: true, 14 | node: true, 15 | jest: true 16 | }, 17 | rules: { 18 | '@typescript-eslint/no-floating-promises': ['error'] 19 | }, 20 | ignorePatterns: ['.eslintrc.js'] 21 | }; 22 | -------------------------------------------------------------------------------- /apps/vc-api/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | pnpm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # OS 15 | .DS_Store 16 | 17 | # Tests 18 | /coverage 19 | /.nyc_output 20 | 21 | # IDEs and editors 22 | /.idea 23 | .project 24 | .classpath 25 | .c9/ 26 | *.launch 27 | .settings/ 28 | *.sublime-workspace 29 | 30 | # IDE - VSCode 31 | .vscode/* 32 | !.vscode/settings.json 33 | !.vscode/tasks.json 34 | !.vscode/launch.json 35 | !.vscode/extensions.json -------------------------------------------------------------------------------- /apps/vc-api/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | For this project's code of conduct, please refer to the Linux Foundation Europe policy document. 4 | A copy of this document is included in this repository as [Exhibit B -- Linux Foundation Europe Policies](./Exhibit%20B%20--%20Linux%20Foundation%20Europe%20Policies.pdf). -------------------------------------------------------------------------------- /apps/vc-api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18.19 as base 2 | 3 | WORKDIR /app 4 | 5 | FROM base as builder 6 | 7 | COPY common common 8 | COPY rush.json . 9 | COPY apps/vc-api/package.json apps/vc-api/package.json 10 | COPY tests/e2e/package.json tests/e2e/package.json 11 | COPY libraries/did/package.json libraries/did/package.json 12 | COPY libraries/webkms/package.json libraries/webkms/package.json 13 | 14 | RUN node common/scripts/install-run-rush.js install 15 | 16 | COPY tsconfig.json . 17 | COPY libraries libraries 18 | COPY apps apps 19 | COPY tests tests 20 | 21 | RUN node common/scripts/install-run-rush.js build 22 | RUN node common/scripts/install-run-rush.js deploy -s ssi-vc-api 23 | 24 | RUN cp -R ./apps/vc-api/dist ./common/deploy/apps/vc-api 25 | 26 | FROM base as final 27 | ENV NODE_ENV=production 28 | EXPOSE 3000 29 | 30 | COPY --from=builder /app/common/deploy /app 31 | COPY license-header.txt /app 32 | 33 | CMD ["node", "apps/vc-api/dist/src/main.js"] 34 | -------------------------------------------------------------------------------- /apps/vc-api/Exhibit B -- Linux Foundation Europe Policies.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwallet-foundation-labs/vc-api/d8cd9adfcc9a30e73682c9728b053f569575ebcc/apps/vc-api/Exhibit B -- Linux Foundation Europe Policies.pdf -------------------------------------------------------------------------------- /apps/vc-api/docs/architecture/decisions/0001-record-architecture-decisions.md: -------------------------------------------------------------------------------- 1 | # 1. Record architecture decisions 2 | 3 | Date: 2024-09-25 4 | 5 | ## Status 6 | 7 | Accepted 8 | 9 | ## Context 10 | 11 | We need to record the architectural decisions made on this project. 12 | 13 | ## Decision 14 | 15 | We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions). 16 | 17 | ## Consequences 18 | 19 | See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools). 20 | -------------------------------------------------------------------------------- /apps/vc-api/docs/architecture/decisions/0002-use-credo-for-key-and-credential-operations.md: -------------------------------------------------------------------------------- 1 | # 2. Use Credo for key and credential operations 2 | 3 | Date: 2024-09-25 4 | 5 | ## Status 6 | 7 | Accepted 8 | 9 | ## Context 10 | 11 | Prior to this decision, Spruce's DIDKit was used for DID generation and credential issuance & verification. 12 | The rational for DIDKit's use is that it: 13 | - Is written in Rust and so suitable for use in any mobile app development framework 14 | - Supports JSON-LD and JWT credential issuance and verification 15 | - Supports did:key, did:ethr, did:web 16 | - DIDKit (and its libraries) are open-source 17 | 18 | However, a significant limitation of DIDKit is that only inline JSON-LD contexts are supported 19 | (see [this discussion on the Spruce Discord server](https://discord.com/channels/862419652286218251/1021707856401141760/1021709845931495444)), 20 | which limits the usability of the VC API application. 21 | 22 | [GitHub Issue #20](https://github.com/openwallet-foundation-labs/vc-api/issues/20) proposed using Credo 23 | to address the JSON-LD context limitation and provide other benefits. 24 | 25 | ## Decision 26 | 27 | Credo (formerly Aries Framework Javascript) is an alternative library for key and credential operations. 28 | We have decided to replace DIDKit with Credo. 29 | 30 | ## Consequences 31 | 32 | Expected benefits from using Credo are: 33 | - The ability to provide additional JSON-LD context documents to a document loader 34 | - Synergy with another Open Wallet Foundation project 35 | - Improved typing, bug patching and debugging due to Credo also being written in Typescript 36 | 37 | A possible risk is that Credo encapsulates the database access 38 | and so the project will have less control over the database schema used for key storage. 39 | -------------------------------------------------------------------------------- /apps/vc-api/docs/architecture/decisions/0003-use-sqlite-for-credo-db.md: -------------------------------------------------------------------------------- 1 | # 3. Use SQLite for Credo DB 2 | 3 | Date: 2024-09-30 4 | 5 | ## Status 6 | 7 | Accepted 8 | 9 | ## Context 10 | 11 | The issue motivating this decision, and any context that influences or constrains the decision. 12 | 13 | [Credo requires a wallet and storage implement](https://credo.js.org/guides/getting-started/set-up#adding-a-wallet-and-storage-implementation). 14 | At the time of writing, only Aries Askar is provided out of the box. 15 | 16 | [Aries Askar](https://github.com/hyperledger/aries-askar) is a database abstract that supports several different underlying databases. 17 | At the time of writing, in-memory (for testing), SQLite and PostgreSQL are supported. 18 | 19 | ## Decision 20 | 21 | The change that we're proposing or have agreed to implement. 22 | 23 | We have decided to use Aries Askar as the underlying storage for Credo. 24 | No other known option was available. 25 | 26 | We have decides to use SQLite 27 | The rationale is twofold: 28 | 1. SQLite is less effort than PostgreSQL for a developer to run the app locally. 29 | 2. SQLite is less effort for an initial integration with Credo. 30 | This decision doesn't preclude the addition of PostgreSQL as an option in the future. 31 | 32 | ## Consequences 33 | 34 | SQLite is superior for experimentation and proof-of-concept but may not be suitable for production usage 35 | depending on scalability requirements. 36 | Support for PostgreSQL as backing store for Credo can be supported in the future. -------------------------------------------------------------------------------- /apps/vc-api/docs/exchanges/example-exchange-definitions/README.md: -------------------------------------------------------------------------------- 1 | A short guide for exchange-definitions examples 2 | 3 | The flows in the different use cases (presentation exchange) can be categorized into three types, which are: 4 | 5 | 1. _Presentation_ - It can be referred to as a presentation / exchange of credentials issued by an authority. For example, `PermanentResidentCard`. 6 | * The interact type would be `Unmediated`. 7 | * The `presentation-definition` in the `credentialQuery` should ask for `PermanentResidentCard` (refer to [presentation-exchange](./presentation-exchange.md) example). 8 | 9 | 2. _Self-sign_ - It can be referred to as a presentation / exchange of a self-signed credential. For example, `ConsentCredential`. The exchange type in this case would be `Unmediated`. 10 | * The interact type would be `Unmediated`. 11 | * The `presentation-definition` in the `credentialQuery` should ask for a self-signed `ConsentCredential` (refer to [self-sign-exchange](./self-signed-exchange.md) example). 12 | 13 | 3. _Issuance_ - It can be referred to as a presentation / exchange needed to issue another credential. 14 | * The interact type would be of type `Mediated`. 15 | * An Issuance always comes after a presentation. 16 | * The presentation needed for issuance can be of type `DIDAuth` or `PresentationDefinition`. 17 | * The `DIDAuth` type exchange is required to prove control over the `DID` to which the credential is being issued (refer to [issuance-exchange](./issuance-exchange.md) example). 18 | * For the exchange of type `PresentationDefinition`, the `credentialQuery` should ask for a credential. In our case, it could be either a self-signed credential i.e., `ConsentCredential` or `PermanentResidentCard`. The issuer gets the `DID` of the subject from the Verifiable Presentation containing the credential (refer to [presentation-issuance-exchange](./presentation-issuance-exchange.md) example). 19 | 20 | Refer to the below specifications for a better understanding: 21 | 22 | * [Presentation Definition](https://identity.foundation/presentation-exchange/#presentation-definition) 23 | * [Submission Requirements](https://identity.foundation/presentation-exchange/#submission-requirement-feature) -------------------------------------------------------------------------------- /apps/vc-api/docs/exchanges/example-exchange-definitions/issuance-exchange.md: -------------------------------------------------------------------------------- 1 | Sample Exchange Definition for issuance. 2 | 3 | Use case: The Holder is required to prove control over DID, thus exchange definition of type `DIDAuth` is required. 4 | 5 | ```json 6 | { 7 | "exchangeId": "286bc1e0-f1bd-488a-a873-8d71be3c690e", 8 | "query": [ 9 | { 10 | "type": "DIDAuth", 11 | "credentialQuery": [] 12 | } 13 | ], 14 | "interactServices": [ 15 | { 16 | "type": "MediatedHttpPresentationService2021" 17 | } 18 | ], 19 | "callback": [ 20 | { 21 | "url": "https://webhook.site/efb19fb8-2579-4e1b-8614-d5a03edaaa7a" 22 | } 23 | ], 24 | "isOneTime":true 25 | } 26 | ``` -------------------------------------------------------------------------------- /apps/vc-api/docs/exchanges/example-exchange-definitions/presentation-exchange.md: -------------------------------------------------------------------------------- 1 | Sample Exchange Definition for a presentation. 2 | 3 | Use case: The Holder is required to present an issued VC (PermanentResidentCard) to the Verifier. 4 | 5 | ``` json 6 | { 7 | "exchangeId":"286bc1e0-f1bd-488a-a873-8d71be3c690e", 8 | "query":[ 9 | { 10 | "type":"PresentationDefinition", 11 | "credentialQuery":[ 12 | { 13 | "presentationDefinition":{ 14 | "id":"286bc1e0-f1bd-488a-a873-8d71be3c690e", 15 | "input_descriptors":[ 16 | { 17 | "id":"permanent_resident_card", 18 | "name":"Permanent Resident Card", 19 | "purpose":"We can only allow permanent residents into the application", 20 | "constraints": { 21 | "fields":[ 22 | { 23 | "path":[ 24 | "$.type" 25 | ], 26 | "filter":{ 27 | "type":"array", 28 | "contains":{ 29 | "type":"string", 30 | "const":"PermanentResidentCard" 31 | } 32 | } 33 | } 34 | ] 35 | } 36 | } 37 | ] 38 | } 39 | } 40 | ] 41 | } 42 | ], 43 | "interactServices":[ 44 | { 45 | "type":"UnmediatedHttpPresentationService2021" 46 | } 47 | ], 48 | "isOneTime":true, 49 | "callback":[ 50 | { 51 | "url": "https://webhook.site/efb19fb8-2579-4e1b-8614-d5a03edaaa7a" 52 | } 53 | ] 54 | } 55 | ``` 56 | -------------------------------------------------------------------------------- /apps/vc-api/docs/exchanges/example-exchange-definitions/presentation-issuance-exchange.md: -------------------------------------------------------------------------------- 1 | Sample Exchange Definition for a presentation and issuance. 2 | 3 | Use case: The Holder is required to present an issued VC (PermanentResidentCard) to the Issuer to obtain another VC. 4 | 5 | ```json 6 | { 7 | "exchangeId":"286bc1e0-f1bd-488a-a873-8d71be3c690e", 8 | "query":[ 9 | { 10 | "type":"PresentationDefinition", 11 | "credentialQuery":[ 12 | { 13 | "presentationDefinition":{ 14 | "id":"286bc1e0-f1bd-488a-a873-8d71be3c690e", 15 | "input_descriptors":[ 16 | { 17 | "id":"permanent_resident_card", 18 | "name":"Permanent Resident Card", 19 | "purpose":"We can only allow permanent residents into the application", 20 | "constraints": { 21 | "fields":[ 22 | { 23 | "path":[ 24 | "$.type" 25 | ], 26 | "filter":{ 27 | "type":"array", 28 | "contains":{ 29 | "type":"string", 30 | "const":"PermanentResidentCard" 31 | } 32 | } 33 | } 34 | ] 35 | } 36 | } 37 | ] 38 | } 39 | } 40 | ] 41 | } 42 | ], 43 | "interactServices":[ 44 | { 45 | "type":"MediatedHttpPresentationService2021" 46 | } 47 | ], 48 | "isOneTime":true, 49 | "callback":[ 50 | { 51 | "url": "https://webhook.site/efb19fb8-2579-4e1b-8614-d5a03edaaa7a" 52 | } 53 | ] 54 | } 55 | ``` -------------------------------------------------------------------------------- /apps/vc-api/docs/vc-api.postman_environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "eaa8dec5-1243-42de-91ea-42ccd48ba76d", 3 | "name": "EWF VC-API", 4 | "values": [ 5 | { 6 | "key": "baseUrl", 7 | "value": "http://localhost:3000", 8 | "enabled": true 9 | } 10 | ], 11 | "_postman_variable_scope": "environment", 12 | "_postman_exported_at": "2022-01-06T22:18:42.474Z", 13 | "_postman_exported_using": "Postman/7.36.5" 14 | } 15 | -------------------------------------------------------------------------------- /apps/vc-api/docs/workflows/example-workflow-definitions/README.md: -------------------------------------------------------------------------------- 1 | A short guide for workflow-definitions examples 2 | 3 | The flows in the different use cases (presentation exchange) can be categorized into three types, which are: 4 | 5 | 1. _Presentation_ - It can be referred to as a presentation / exchange of credentials issued by an authority. For example, `PermanentResidentCard`. 6 | * The interact type would be `Unmediated`. 7 | * The `presentation-definition` in the `credentialQuery` should ask for `PermanentResidentCard` (refer to [presentation-workflow](./presentation-workflow.md) example). 8 | 9 | 2. _Self-sign_ - It can be referred to as a presentation / workflow of a self-signed credential. For example, `ConsentCredential`. The workflow type in this case would be `Unmediated`. 10 | * The interact type would be `Unmediated`. 11 | * The `presentation-definition` in the `credentialQuery` should ask for a self-signed `ConsentCredential` (refer to [self-sign-workflow](./self-signed-workflow.md) example). 12 | 13 | 3. _Issuance_ - It can be referred to as a presentation / workflow needed to issue another credential. 14 | * The interact type would be of type `Mediated`. 15 | * An Issuance always comes after a presentation. 16 | * The presentation needed for issuance can be of type `DIDAuth` or `PresentationDefinition`. 17 | * The `DIDAuth` type workflow is required to prove control over the `DID` to which the credential is being issued (refer to [issuance-workflow](./issuance-workflow.md) example). 18 | * For the workflow of type `PresentationDefinition`, the `credentialQuery` should ask for a credential. In our case, it could be either a self-signed credential i.e., `ConsentCredential` or `PermanentResidentCard`. The issuer gets the `DID` of the subject from the Verifiable Presentation containing the credential (refer to [presentation-issuance-workflow](./presentation-issuance-workflow.md) example). 19 | 20 | Refer to the below specifications for a better understanding: 21 | 22 | * [Presentation Definition](https://identity.foundation/presentation-exchange/#presentation-definition) 23 | * [Submission Requirements](https://identity.foundation/presentation-exchange/#submission-requirement-feature) -------------------------------------------------------------------------------- /apps/vc-api/docs/workflows/example-workflow-definitions/issuance-workflow.md: -------------------------------------------------------------------------------- 1 | Sample Workflow Definition for Issuance. 2 | 3 | Use case: The Holder is required to prove control over DID, thus query of type `DIDAuth` is used. 4 | 5 | ```json 6 | { 7 | "config": { 8 | "id": "workflowId", 9 | "steps": { 10 | "didAuth": { 11 | "verifiablePresentationRequest": { 12 | "query": [ 13 | { 14 | "type": "DIDAuth", 15 | "credentialQuery": [] 16 | } 17 | ], 18 | "interactServices": [ 19 | { 20 | "type": "UnmediatedHttpPresentationService2021" 21 | } 22 | ] 23 | }, 24 | "callback": [ 25 | { 26 | "url": "callbackUrl" 27 | } 28 | ], 29 | "nextStep": "residentCardIssuance" 30 | }, 31 | "residentCardIssuance": { 32 | "callback": [ 33 | { 34 | "url": "callbackUrl" 35 | } 36 | ] 37 | } 38 | }, 39 | "initialStep": "didAuth" 40 | } 41 | } 42 | ``` -------------------------------------------------------------------------------- /apps/vc-api/docs/workflows/example-workflow-definitions/presentation-exchange.md: -------------------------------------------------------------------------------- 1 | Sample Workflow Definition for a presentation. 2 | 3 | Use case: The Holder is required to present an issued VC (PermanentResidentCard) to the Verifier. 4 | 5 | ``` json 6 | { 7 | "config": { 8 | "id": "workflowId", 9 | "steps": { 10 | "presentation": { 11 | "verifiablePresentationRequest": { 12 | "query": [ 13 | { 14 | "type": "PresentationDefinition", 15 | "credentialQuery": [ 16 | { 17 | "presentationDefinition": { 18 | "id": "286bc1e0-f1bd-488a-a873-8d71be3c690e", 19 | "input_descriptors": [ 20 | { 21 | "id": "permanent_resident_card", 22 | "name": "Permanent Resident Card", 23 | "purpose": "We can only allow permanent residents into the application", 24 | "constraints": { 25 | "fields": [ 26 | { 27 | "path": [ 28 | "$.type" 29 | ], 30 | "filter": { 31 | "type": "array", 32 | "contains": { 33 | "type": "string", 34 | "const": "PermanentResidentCard" 35 | } 36 | } 37 | } 38 | ] 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | ] 45 | } 46 | ], 47 | "interactServices": [ 48 | { 49 | "type": "UnmediatedHttpPresentationService2021" 50 | } 51 | ] 52 | }, 53 | "callback": [ 54 | { 55 | "url": "callbackUrl" 56 | } 57 | ] 58 | } 59 | }, 60 | "initialStep": "presentation" 61 | } 62 | } 63 | ``` 64 | -------------------------------------------------------------------------------- /apps/vc-api/docs/workflows/example-workflow-definitions/presentation-issuance-workflow.md: -------------------------------------------------------------------------------- 1 | Sample Workflow Definition for a presentation and issuance. 2 | 3 | Use case: The Holder is required to present an issued VC (PermanentResidentCard) to the Issuer to obtain another VC. 4 | 5 | ```json 6 | { 7 | "config": { 8 | "id": "workflowId", 9 | "steps": { 10 | "presentation": { 11 | "verifiablePresentationRequest": { 12 | "query": [ 13 | { 14 | "type": "PresentationDefinition", 15 | "credentialQuery": [ 16 | { 17 | "presentationDefinition": { 18 | "id": "286bc1e0-f1bd-488a-a873-8d71be3c690e", 19 | "input_descriptors": [ 20 | { 21 | "id": "permanent_resident_card", 22 | "name": "Permanent Resident Card", 23 | "purpose": "We can only allow permanent residents into the application", 24 | "constraints": { 25 | "fields": [ 26 | { 27 | "path": [ 28 | "$.type" 29 | ], 30 | "filter": { 31 | "type": "array", 32 | "contains": { 33 | "type": "string", 34 | "const": "PermanentResidentCard" 35 | } 36 | } 37 | } 38 | ] 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | ] 45 | } 46 | ], 47 | "interactServices": [ 48 | { 49 | "type": "UnmediatedHttpPresentationService2021" 50 | } 51 | ] 52 | }, 53 | "callback": [ 54 | { 55 | "url": "callbackUrl" 56 | } 57 | ], 58 | "nextStep": "residentCardIssuance" 59 | }, 60 | "residentCardIssuance": { 61 | "callback": [ 62 | { 63 | "url": "callbackUrl" 64 | } 65 | ] 66 | } 67 | }, 68 | "initialStep": "presentation" 69 | } 70 | } 71 | ``` -------------------------------------------------------------------------------- /apps/vc-api/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src", 4 | "compilerOptions": { 5 | "plugins": [] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/vc-api/scripts/write-open-api-json.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { writeFileSync } from 'fs'; 7 | import * as path from 'path'; 8 | import { API_DEFAULT_VERSION, setupApp, setupSwaggerDocument } from '../src/setup'; 9 | import { Test } from '@nestjs/testing'; 10 | import { AppModule } from '../src'; 11 | import { VersioningType } from '@nestjs/common'; 12 | 13 | /** 14 | * https://stackoverflow.com/questions/64927411/how-to-generate-openapi-specification-with-nestjs-without-running-the-applicatio 15 | */ 16 | (async () => { 17 | const module = await Test.createTestingModule({ 18 | imports: [AppModule] 19 | }).compile(); 20 | 21 | const app = module.createNestApplication(undefined, { logger: false }); 22 | 23 | app.enableVersioning({ 24 | type: VersioningType.URI, 25 | defaultVersion: [API_DEFAULT_VERSION] 26 | }); 27 | 28 | const doc = setupSwaggerDocument(app); 29 | const outputPath = path.resolve(process.cwd(), 'docs', 'openapi.json'); 30 | writeFileSync(outputPath, JSON.stringify(doc), { encoding: 'utf8' }); 31 | console.log('open-api json written'); 32 | })(); 33 | -------------------------------------------------------------------------------- /apps/vc-api/src/app.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { DynamicModule, MiddlewareConsumer, Module } from '@nestjs/common'; 7 | import { ConfigModule } from '@nestjs/config'; 8 | import { KeyModule } from './key/key.module'; 9 | import { DidModule } from './did/did.module'; 10 | import { VcApiModule } from './vc-api/vc-api.module'; 11 | import { TypeOrmSQLiteModule } from './db-config'; 12 | import { ServeStaticModule } from '@nestjs/serve-static'; 13 | import { join } from 'path'; 14 | import { SeederModule } from './seeder/seeder.module'; 15 | import { envVarsValidationSchema } from './config/env-vars-validation-schema'; 16 | import { HttpLoggerMiddleware } from './middlewares'; 17 | import { CredoModule } from './credo/credo.module'; 18 | 19 | let config: DynamicModule; 20 | 21 | try { 22 | config = ConfigModule.forRoot({ 23 | isGlobal: true, 24 | validationOptions: { 25 | allowUnknown: true, 26 | abortEarly: false 27 | }, 28 | validationSchema: envVarsValidationSchema 29 | }); 30 | } catch (err) { 31 | console.log(err.toString()); 32 | console.log('exiting'); 33 | // eslint-disable-next-line no-process-exit 34 | process.exit(1); 35 | } 36 | 37 | @Module({ 38 | imports: [ 39 | TypeOrmSQLiteModule(), 40 | KeyModule, 41 | DidModule, 42 | VcApiModule, 43 | CredoModule, 44 | config, 45 | ServeStaticModule.forRoot({ 46 | rootPath: join(__dirname, '../..', 'static-assets', '.well-known'), 47 | serveStaticOptions: { 48 | index: false 49 | }, 50 | serveRoot: '/.well-known' 51 | }), 52 | SeederModule 53 | ] 54 | }) 55 | export class AppModule { 56 | configure(consumer: MiddlewareConsumer) { 57 | consumer 58 | .apply(HttpLoggerMiddleware) //, HttpsRedirectMiddleware) - Disabling for now, doesn't work as expected 59 | .forRoutes('*'); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /apps/vc-api/src/config/env-vars-validation-schema.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import * as Joi from 'joi'; 7 | import { homedir } from 'os'; 8 | 9 | export const envVarsValidationSchema = Joi.object({ 10 | NODE_ENV: Joi.string().valid('development', 'production', 'test').default('development'), 11 | PORT: Joi.number().integer().positive().default(3000), 12 | BASE_URL: Joi.string().uri().default('http://localhost:3000'), 13 | CREDO_LABEL: Joi.string().default('vc-api-agent'), 14 | CREDO_WALLET_ID: Joi.string().default('vc-api-wallet'), 15 | CREDO_WALLET_KEY: Joi.string().default('vc-api-wallet-key-0001'), 16 | CREDO_WALLET_DB_TYPE: Joi.string().default('sqlite'), 17 | DB_BASE_PATH: Joi.string().default(`${homedir}/.vc-api`) 18 | }); 19 | -------------------------------------------------------------------------------- /apps/vc-api/src/credo/__mocks__/credo.service.ts: -------------------------------------------------------------------------------- 1 | export const mockCredoService = { 2 | wallet: { 3 | withSession: jest.fn() 4 | }, 5 | agent: { 6 | wallet: { 7 | createKey: jest.fn() 8 | }, 9 | w3cCredentials: { 10 | signCredential: jest.fn(), 11 | verifyCredential: jest.fn(), 12 | signPresentation: jest.fn(), 13 | verifyPresentation: jest.fn() 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /apps/vc-api/src/credo/credo.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, Global } from '@nestjs/common'; 2 | import { ConfigModule } from '@nestjs/config'; 3 | import { CredoService } from './credo.service'; 4 | 5 | @Global() 6 | @Module({ 7 | imports: [ConfigModule], 8 | providers: [CredoService], 9 | exports: [CredoService] 10 | }) 11 | export class CredoModule {} 12 | -------------------------------------------------------------------------------- /apps/vc-api/src/db-config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { TypeOrmModule } from '@nestjs/typeorm'; 7 | import { ConfigModule, ConfigService } from '@nestjs/config'; 8 | 9 | /** 10 | * Inspired by https://dev.to/webeleon/unit-testing-nestjs-with-typeorm-in-memory-l6m 11 | * @param inMemory // default set to false; set to true for unit testing 12 | */ 13 | export const TypeOrmSQLiteModule = (inMemory = false) => 14 | TypeOrmModule.forRootAsync({ 15 | imports: [ConfigModule], 16 | useFactory: async (configService: ConfigService) => ({ 17 | type: 'sqlite', 18 | database: !inMemory ? `${configService.get('DB_BASE_PATH')}/exchanges.db` : ':memory:', 19 | // dropSchema: true, // disabling this option to avoid dropping askar tables 20 | autoLoadEntities: true, // https://docs.nestjs.com/techniques/database#auto-load-entities 21 | synchronize: true, 22 | keepConnectionAlive: true // https://github.com/nestjs/typeorm/issues/61 23 | }), 24 | inject: [ConfigService] 25 | }); 26 | -------------------------------------------------------------------------------- /apps/vc-api/src/did/did.controller.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Test, TestingModule } from '@nestjs/testing'; 7 | import { TypeOrmModule } from '@nestjs/typeorm'; 8 | import { TypeOrmSQLiteModule } from '../db-config'; 9 | import { KeyModule } from '../key/key.module'; 10 | import { DIDController } from './did.controller'; 11 | import { DIDService } from './did.service'; 12 | import { DIDDocumentEntity } from './entities/did-document.entity'; 13 | import { VerificationMethodEntity } from './entities/verification-method.entity'; 14 | import { CredoModule } from '../credo/credo.module'; 15 | 16 | describe('DidController', () => { 17 | let controller: DIDController; 18 | 19 | beforeEach(async () => { 20 | const module: TestingModule = await Test.createTestingModule({ 21 | imports: [ 22 | KeyModule, 23 | CredoModule, 24 | TypeOrmSQLiteModule(true), 25 | TypeOrmModule.forFeature([DIDDocumentEntity, VerificationMethodEntity]) 26 | ], 27 | controllers: [DIDController], 28 | providers: [DIDService] 29 | }).compile(); 30 | 31 | controller = module.get(DIDController); 32 | }); 33 | 34 | it('should be defined', () => { 35 | expect(controller).toBeDefined(); 36 | }); 37 | 38 | // describe('create', () => { 39 | // it('should create an ethr DID', async () => { 40 | // await controller.create({ method: DidMethod.ethr }); 41 | // }); 42 | // }); 43 | }); 44 | -------------------------------------------------------------------------------- /apps/vc-api/src/did/did.controller.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { BadRequestException, Body, Controller, Get, NotFoundException, Param, Post } from '@nestjs/common'; 7 | import { DIDService } from './did.service'; 8 | import { 9 | ApiBadRequestResponse, 10 | ApiBody, 11 | ApiCreatedResponse, 12 | ApiInternalServerErrorResponse, 13 | ApiNotFoundResponse, 14 | ApiOkResponse, 15 | ApiOperation, 16 | ApiTags 17 | } from '@nestjs/swagger'; 18 | import { CreateDidOptionsDto } from './dto/create-did-options.dto'; 19 | import { DidMethod } from './types/did-method'; 20 | import { CreateDidResponseDto } from './dto/create-did-response.dto'; 21 | import { BadRequestErrorResponseDto } from '../dtos/bad-request-error-response.dto'; 22 | import { NotFoundErrorResponseDto } from '../dtos/not-found-error-response.dto'; 23 | import { InternalServerErrorResponseDto } from '../dtos/internal-server-error-response.dto'; 24 | 25 | @ApiTags('did') 26 | @Controller('did') 27 | @ApiInternalServerErrorResponse({ type: InternalServerErrorResponseDto }) 28 | export class DIDController { 29 | constructor(private didService: DIDService) {} 30 | 31 | @Post() 32 | @ApiBody({ type: CreateDidOptionsDto }) 33 | @ApiCreatedResponse({ type: CreateDidResponseDto }) 34 | @ApiBadRequestResponse({ type: BadRequestErrorResponseDto }) 35 | @ApiOperation({ description: 'Generate a new DID' }) 36 | async create(@Body() body: CreateDidOptionsDto): Promise { 37 | if (body.method === DidMethod.ethr) { 38 | return new CreateDidResponseDto(await this.didService.generateEthrDID()); 39 | } 40 | if (body.method === DidMethod.key) { 41 | if (body.keyId) { 42 | return await this.didService.registerKeyDID(body.keyId); 43 | } 44 | return new CreateDidResponseDto(await this.didService.generateKeyDID()); 45 | } 46 | throw new BadRequestException('Requested DID method not supported'); 47 | } 48 | 49 | @Get('/:did') 50 | @ApiOkResponse({ type: CreateDidResponseDto }) 51 | @ApiNotFoundResponse({ type: NotFoundErrorResponseDto }) 52 | @ApiOperation({ description: 'Retrieve exisiting DID' }) 53 | async getByDID(@Param('did') did: string): Promise { 54 | const didDoc = await this.didService.getDID(did); 55 | 56 | if (!didDoc) { 57 | throw new NotFoundException(`${did} not found`); 58 | } 59 | 60 | return new CreateDidResponseDto(didDoc); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /apps/vc-api/src/did/did.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Module } from '@nestjs/common'; 7 | import { TypeOrmModule } from '@nestjs/typeorm'; 8 | import { KeyModule } from '../key/key.module'; 9 | import { DIDController } from './did.controller'; 10 | import { DIDService } from './did.service'; 11 | import { DIDDocumentEntity } from './entities/did-document.entity'; 12 | import { VerificationMethodEntity } from './entities/verification-method.entity'; 13 | 14 | @Module({ 15 | imports: [KeyModule, TypeOrmModule.forFeature([DIDDocumentEntity, VerificationMethodEntity])], 16 | controllers: [DIDController], 17 | providers: [DIDService], 18 | exports: [DIDService] 19 | }) 20 | export class DidModule {} 21 | -------------------------------------------------------------------------------- /apps/vc-api/src/did/dto/create-did-options.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsEnum, IsOptional, IsString } from 'class-validator'; 7 | import { DidMethod } from '../types/did-method'; 8 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 9 | 10 | export class CreateDidOptionsDto { 11 | @IsEnum(DidMethod) 12 | @ApiProperty({ 13 | description: 'DID Method to create.\nMust be one of "key" or "ethr"', 14 | enum: DidMethod, 15 | enumName: 'DidMethod' 16 | }) 17 | method: DidMethod; 18 | 19 | @IsString() 20 | @IsOptional() 21 | @ApiPropertyOptional({ 22 | description: 23 | 'id of key (for example, JWK thumbprint).\n' + 24 | 'This key must be known to the server already.\n' + 25 | 'If provided, DID will be created using this key.\n' + 26 | 'Currently only supported for did:key.' 27 | }) 28 | keyId?: string; 29 | } 30 | -------------------------------------------------------------------------------- /apps/vc-api/src/did/dto/create-did-response.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 7 | import { VerificationMethodDto } from './verification-method.dto'; 8 | import { Type } from 'class-transformer'; 9 | 10 | export class CreateDidResponseDto { 11 | @ApiProperty() 12 | id: string; 13 | 14 | @ApiPropertyOptional({ type: VerificationMethodDto, isArray: true }) 15 | @Type(() => VerificationMethodDto) 16 | verificationMethod?: VerificationMethodDto[]; 17 | 18 | constructor(partial: Partial) { 19 | Object.assign(this, partial); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/vc-api/src/did/dto/json-web-key.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 7 | 8 | export class JsonWebKeyDto { 9 | @ApiPropertyOptional() 10 | alg?: string; 11 | 12 | @ApiPropertyOptional() 13 | crv?: string; 14 | 15 | @ApiPropertyOptional() 16 | e?: string; 17 | 18 | @ApiPropertyOptional() 19 | ext?: boolean; 20 | 21 | @ApiPropertyOptional() 22 | key_ops?: string[]; 23 | 24 | @ApiPropertyOptional() 25 | kid?: string; 26 | 27 | @ApiProperty() 28 | kty: string; 29 | 30 | @ApiPropertyOptional() 31 | n?: string; 32 | 33 | @ApiPropertyOptional() 34 | use?: string; 35 | 36 | @ApiPropertyOptional() 37 | x?: string; 38 | 39 | @ApiPropertyOptional() 40 | y?: string; 41 | 42 | constructor(partial: Partial) { 43 | Object.assign(this, partial); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /apps/vc-api/src/did/dto/verification-method.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 7 | import { JsonWebKeyDto } from './json-web-key.dto'; 8 | import { Type } from 'class-transformer'; 9 | 10 | export class VerificationMethodDto { 11 | @ApiProperty() 12 | id: string; 13 | 14 | @ApiProperty() 15 | type: string; 16 | 17 | @ApiProperty() 18 | controller: string; 19 | 20 | @ApiPropertyOptional({ type: JsonWebKeyDto }) 21 | @Type(() => JsonWebKeyDto) 22 | publicKeyJwk?: JsonWebKeyDto; 23 | 24 | constructor(partial: Partial) { 25 | Object.assign(this, partial); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/vc-api/src/did/entities/did-document.entity.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Column, Entity, OneToMany } from 'typeorm'; 7 | import { VerificationMethodEntity } from './verification-method.entity'; 8 | 9 | /** 10 | * A TypeOrm entity representing a DID Document 11 | * Should conform to https://www.w3.org/TR/did-core 12 | */ 13 | @Entity() 14 | export class DIDDocumentEntity { 15 | /** 16 | * From https://www.w3.org/TR/did-core/#did-subject : 17 | * "The DID for a particular DID subject is expressed using the id property in the DID document. 18 | * The value of id MUST be a string that conforms to the rules in § 3.1 DID Syntax" 19 | */ 20 | @Column('text', { primary: true }) 21 | id: string; 22 | 23 | /** 24 | * From: https://www.w3.org/TR/did-core/#verification-methods : 25 | * "A DID document can express verification methods, such as cryptographic public keys, 26 | * which can be used to authenticate or authorize interactions with the DID subject or associated parties. 27 | * For example, a cryptographic public key can be used as a verification method with respect to a digital signature;" 28 | */ 29 | @OneToMany(() => VerificationMethodEntity, (verificationMethod) => verificationMethod.didDoc, { 30 | cascade: true 31 | }) 32 | verificationMethod: VerificationMethodEntity[]; 33 | } 34 | -------------------------------------------------------------------------------- /apps/vc-api/src/did/entities/verification-method.entity.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { DifJsonWebKey } from '@energyweb/ssi-did'; 7 | import { VerificationMethod } from 'did-resolver'; 8 | import { Entity, Column, ManyToOne } from 'typeorm'; 9 | import { DIDDocumentEntity } from './did-document.entity'; 10 | 11 | /** 12 | * An entity allowing storage of Verification Methods https://www.w3.org/TR/did-core/#verification-methods 13 | * See https://www.w3.org/TR/did-core/#example-various-verification-method-types for example 14 | */ 15 | @Entity() 16 | export class VerificationMethodEntity implements VerificationMethod { 17 | /** 18 | * From https://www.w3.org/TR/did-core/#verification-methods : 19 | * "It is RECOMMENDED that verification methods that use JWKs [RFC7517] to represent their public keys use the value of kid as their fragment identifier." 20 | */ 21 | @Column('text', { primary: true }) 22 | id: string; 23 | 24 | /** 25 | * From https://www.w3.org/TR/did-core/#verification-methods : 26 | * "The type of a verification method is expected to be used to determine its compatibility with such processes [that apply a verification method]" 27 | */ 28 | @Column('text') 29 | type: string; 30 | 31 | /** 32 | * From https://www.w3.org/TR/did-core/#verification-methods : 33 | * "The value of the controller property MUST be a string that conforms to the rules in § 3.1 DID Syntax." 34 | */ 35 | @Column('text') 36 | controller: string; 37 | 38 | @Column('simple-json') 39 | publicKeyJwk: DifJsonWebKey; 40 | 41 | /** 42 | * The DID Documents that reference this verification method 43 | * In principle, a verification method could be used by many DIDs? (to be confirmed) 44 | */ 45 | @ManyToOne(() => DIDDocumentEntity, (didDoc) => didDoc.verificationMethod) 46 | didDoc: DIDDocumentEntity; 47 | } 48 | -------------------------------------------------------------------------------- /apps/vc-api/src/did/types/did-method.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | export enum DidMethod { 7 | key = 'key', 8 | ethr = 'ethr' 9 | } 10 | -------------------------------------------------------------------------------- /apps/vc-api/src/dtos/bad-request-error-response.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ApiProperty } from '@nestjs/swagger'; 7 | 8 | export class BadRequestErrorResponseDto { 9 | @ApiProperty({ example: 400 }) 10 | statusCode: 400; 11 | 12 | @ApiProperty({ 13 | oneOf: [ 14 | { 15 | type: 'array', 16 | items: { type: 'string' }, 17 | example: ['error 1', 'error 2'] 18 | }, 19 | { type: 'string', example: 'error' } 20 | ] 21 | }) 22 | message: string | string[]; 23 | 24 | @ApiProperty({ example: 'Bad Request' }) 25 | error: 'Bad Request'; 26 | } 27 | -------------------------------------------------------------------------------- /apps/vc-api/src/dtos/conflict-error-response.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ApiProperty } from '@nestjs/swagger'; 7 | 8 | export class ConflictErrorResponseDto { 9 | @ApiProperty({ example: 409 }) 10 | statusCode: 409; 11 | 12 | @ApiProperty() 13 | message: string; 14 | 15 | @ApiProperty({ example: 'Conflict' }) 16 | error: 'Conflict'; 17 | } 18 | -------------------------------------------------------------------------------- /apps/vc-api/src/dtos/internal-server-error-response.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 7 | 8 | export class InternalServerErrorResponseDto { 9 | @ApiProperty({ example: 500 }) 10 | statusCode: 500; 11 | 12 | @ApiPropertyOptional({ 13 | oneOf: [ 14 | { type: 'string', example: 'error' }, 15 | { 16 | type: 'array', 17 | items: { type: 'string' }, 18 | example: ['error 1', 'error 2'] 19 | } 20 | ] 21 | }) 22 | message?: string | string[]; 23 | 24 | @ApiProperty({ example: 'Internal Server Error' }) 25 | error: 'Internal Server Error'; 26 | } 27 | -------------------------------------------------------------------------------- /apps/vc-api/src/dtos/not-found-error-response.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ApiProperty } from '@nestjs/swagger'; 7 | 8 | export class NotFoundErrorResponseDto { 9 | @ApiProperty({ example: 404 }) 10 | statusCode: 404; 11 | 12 | @ApiProperty() 13 | message: string; 14 | 15 | @ApiProperty({ example: 'Not Found' }) 16 | error: 'Not Found'; 17 | } 18 | -------------------------------------------------------------------------------- /apps/vc-api/src/exception-filters/transaction-entity-exception.filter.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { BaseExceptionFilter } from '@nestjs/core'; 7 | import { TransactionEntityException } from '../vc-api/exchanges/entities/transaction.entity'; 8 | import { ArgumentsHost, Catch, ForbiddenException, Logger } from '@nestjs/common'; 9 | 10 | @Catch(TransactionEntityException) 11 | export class TransactionEntityExceptionFilter extends BaseExceptionFilter { 12 | private readonly logger: Logger = new Logger(TransactionEntityExceptionFilter.name, { timestamp: true }); 13 | 14 | catch(exception: TransactionEntityException, host: ArgumentsHost): void { 15 | switch (exception.name) { 16 | case 'TransactionDidForbiddenException': 17 | this.logger.warn(exception); 18 | super.catch(new ForbiddenException(exception.message), host); 19 | break; 20 | 21 | default: 22 | this.logger.error(exception); 23 | super.catch(exception, host); 24 | break; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/vc-api/src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | export { AppModule } from './app.module'; 7 | -------------------------------------------------------------------------------- /apps/vc-api/src/key/dtos/key-description.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IKeyDescription } from '@energyweb/w3c-ccg-webkms'; 7 | import { IsString } from 'class-validator'; 8 | import { ApiProperty } from '@nestjs/swagger'; 9 | 10 | /** 11 | * KeyPair 12 | */ 13 | export class KeyDescriptionDto implements IKeyDescription { 14 | @IsString() 15 | @ApiProperty({ description: 'id of key (for example, public key base58 string)' }) 16 | public keyId: string; 17 | } 18 | -------------------------------------------------------------------------------- /apps/vc-api/src/key/dtos/key-pair.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsNotEmptyObject } from 'class-validator'; 7 | import { JWK } from 'jose'; 8 | import { ApiProperty } from '@nestjs/swagger'; 9 | 10 | /** 11 | * KeyPair 12 | */ 13 | export class KeyPairDto { 14 | @IsNotEmptyObject() 15 | @ApiProperty({ description: 'private key JWK' }) 16 | // TODO: decide if to reuse JsonWebKeyDto or define a new Dto class with more fields like in JWK interface from jose 17 | public privateKey: JWK; 18 | 19 | @IsNotEmptyObject() 20 | @ApiProperty({ description: 'public key JWK' }) 21 | public publicKey: JWK; 22 | } 23 | -------------------------------------------------------------------------------- /apps/vc-api/src/key/key-pair.entity.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { JWK } from 'jose'; 7 | import { Entity, Column } from 'typeorm'; 8 | 9 | /** 10 | * An entity representing a stored KeyPair 11 | */ 12 | @Entity() 13 | export class KeyPair { 14 | @Column('text', { primary: true }) 15 | public publicKeyThumbprint: string; 16 | 17 | @Column('simple-json') 18 | public privateKey: JWK; 19 | 20 | @Column('simple-json') 21 | public publicKey: JWK; 22 | } 23 | -------------------------------------------------------------------------------- /apps/vc-api/src/key/key-types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | export enum keyType { 7 | secp256k1 = 'secp256k1', 8 | ed25519 = 'ed25519' 9 | } 10 | -------------------------------------------------------------------------------- /apps/vc-api/src/key/key.controller.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Test, TestingModule } from '@nestjs/testing'; 7 | import { KeyController } from './key.controller'; 8 | import { KeyService } from './key.service'; 9 | 10 | describe('KeyController', () => { 11 | let controller: KeyController; 12 | 13 | beforeEach(async () => { 14 | const module: TestingModule = await Test.createTestingModule({ 15 | controllers: [KeyController], 16 | providers: [ 17 | { 18 | provide: KeyService, 19 | useValue: {} 20 | } 21 | ] 22 | }).compile(); 23 | 24 | controller = module.get(KeyController); 25 | }); 26 | 27 | it('should be defined', () => { 28 | expect(controller).toBeDefined(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /apps/vc-api/src/key/key.controller.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { BadRequestException, Body, Controller, Get, NotFoundException, Param, Post } from '@nestjs/common'; 7 | import { 8 | ApiBadRequestResponse, 9 | ApiBody, 10 | ApiCreatedResponse, 11 | ApiInternalServerErrorResponse, 12 | ApiNotFoundResponse, 13 | ApiOkResponse, 14 | ApiOperation, 15 | ApiTags 16 | } from '@nestjs/swagger'; 17 | import { validate } from 'class-validator'; 18 | import { KeyDescriptionDto } from './dtos/key-description.dto'; 19 | import { KeyPairDto } from './dtos/key-pair.dto'; 20 | import { KeyService } from './key.service'; 21 | import { BadRequestErrorResponseDto } from '../dtos/bad-request-error-response.dto'; 22 | import { NotFoundErrorResponseDto } from '../dtos/not-found-error-response.dto'; 23 | import { InternalServerErrorResponseDto } from '../dtos/internal-server-error-response.dto'; 24 | 25 | @ApiTags('key') 26 | @Controller('key') 27 | @ApiInternalServerErrorResponse({ type: InternalServerErrorResponseDto }) 28 | export class KeyController { 29 | constructor(private keyService: KeyService) {} 30 | 31 | /** 32 | * Import a key 33 | * @param body key pair to import 34 | * @returns KeyDescription of imported key 35 | */ 36 | @Post() 37 | @ApiBody({ type: KeyPairDto }) 38 | @ApiCreatedResponse({ type: KeyDescriptionDto }) 39 | @ApiBadRequestResponse({ type: BadRequestErrorResponseDto }) 40 | @ApiOperation({ description: 'Import a key' }) 41 | async import(@Body() body: KeyPairDto): Promise { 42 | return await this.keyService.importKey(body); 43 | } 44 | 45 | @Get('/:keyId') 46 | @ApiOkResponse({ type: KeyPairDto }) 47 | @ApiNotFoundResponse({ type: NotFoundErrorResponseDto }) 48 | async export(@Param('keyId') keyId: string): Promise { 49 | const keyDescription = new KeyDescriptionDto(); 50 | keyDescription.keyId = keyId; 51 | try { 52 | await validate(keyDescription); 53 | } catch (error) { 54 | throw new BadRequestException(error); 55 | } 56 | const key = await this.keyService.exportKey(keyDescription); 57 | 58 | if (!key) { 59 | throw new NotFoundException(`keyId=${keyId} not found`); 60 | } 61 | 62 | return key; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /apps/vc-api/src/key/key.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Module } from '@nestjs/common'; 7 | import { TypeOrmModule } from '@nestjs/typeorm'; 8 | import { KeyPair } from './key-pair.entity'; 9 | import { KeyService } from './key.service'; 10 | import { KeyController } from './key.controller'; 11 | 12 | @Module({ 13 | imports: [TypeOrmModule.forFeature([KeyPair])], 14 | providers: [KeyService], 15 | exports: [KeyService], 16 | controllers: [KeyController] 17 | }) 18 | export class KeyModule {} 19 | -------------------------------------------------------------------------------- /apps/vc-api/src/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { setupApp, setupSwaggerDocument } from './setup'; 7 | import { readFile } from 'fs/promises'; 8 | import { resolve as resolvePath } from 'path'; 9 | import { SwaggerModule } from '@nestjs/swagger'; 10 | import { SeederService } from './seeder/seeder.service'; 11 | import * as process from 'process'; 12 | 13 | async function bootstrap() { 14 | const app = await setupApp(); 15 | SwaggerModule.setup('api', app, setupSwaggerDocument(app)); 16 | await app.listen(process.env.PORT); 17 | 18 | // Seeding needed in order to be able to execute VC-API test suite 19 | // https://github.com/w3c-ccg/vc-api-test-suite 20 | await app.get(SeederService).seed(); 21 | 22 | try { 23 | console.log( 24 | '\n' + 25 | (await readFile(resolvePath(__dirname, '..', '..', '..', '..', 'license-header.txt'))).toString() + 26 | '\n' 27 | ); 28 | } catch (err) { 29 | console.error(`license file not found: ${err}`); 30 | } 31 | } 32 | 33 | void bootstrap(); 34 | -------------------------------------------------------------------------------- /apps/vc-api/src/middlewares/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | export * from './http-logger.middleware'; 7 | -------------------------------------------------------------------------------- /apps/vc-api/src/seeder/fixtures/key-pair.fixture.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { KeyPair } from '../../key/key-pair.entity'; 7 | 8 | export const keyPairFixture: KeyPair[] = [ 9 | { 10 | publicKeyThumbprint: 'Cb-dMbY5dpIuJiEAZpSFmMS45xG9U1IGTZ6fu-0_Y2Q', 11 | privateKey: { 12 | crv: 'Ed25519', 13 | d: 'rp_w8CI32yfCiF-C-8sX9nBjss7DVw6rJD-OOAkG4Jw', 14 | x: 'xlIb-qSN_fdmZNvM17C9QoW8qJ_kSFoyD2O534vNAbM', 15 | kty: 'OKP' 16 | }, 17 | publicKey: { 18 | crv: 'Ed25519', 19 | x: 'xlIb-qSN_fdmZNvM17C9QoW8qJ_kSFoyD2O534vNAbM', 20 | kty: 'OKP', 21 | kid: 'Cb-dMbY5dpIuJiEAZpSFmMS45xG9U1IGTZ6fu-0_Y2Q' 22 | } 23 | }, 24 | { 25 | publicKeyThumbprint: 'DnxN3-MVQwLsGPxI_XSeOxpsirM7RJYYtyzDjWGi1hc', 26 | privateKey: { 27 | crv: 'Ed25519', 28 | d: 'zH2tR4S1_g1ETHatMu1dzC2AJ7pO8L_iPrAYbxrh4qs', 29 | x: 'QJVXmSVxPrIRx5KlZwx0Zi_8K12zf4BXj_GWwSu0Ep8', 30 | kty: 'OKP' 31 | }, 32 | publicKey: { 33 | crv: 'Ed25519', 34 | x: 'QJVXmSVxPrIRx5KlZwx0Zi_8K12zf4BXj_GWwSu0Ep8', 35 | kty: 'OKP', 36 | kid: 'DnxN3-MVQwLsGPxI_XSeOxpsirM7RJYYtyzDjWGi1hc' 37 | } 38 | } 39 | ]; 40 | -------------------------------------------------------------------------------- /apps/vc-api/src/seeder/seeder.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Module } from '@nestjs/common'; 7 | import { SeederService } from './seeder.service'; 8 | import { KeyPair } from '../key/key-pair.entity'; 9 | import { TypeOrmModule } from '@nestjs/typeorm'; 10 | import { DidModule } from '../did/did.module'; 11 | import { KeyModule } from '../key/key.module'; 12 | 13 | @Module({ 14 | imports: [TypeOrmModule.forFeature([KeyPair]), DidModule, KeyModule], 15 | providers: [SeederService] 16 | }) 17 | export class SeederModule {} 18 | -------------------------------------------------------------------------------- /apps/vc-api/src/seeder/seeder.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Injectable, Logger } from '@nestjs/common'; 7 | import { keyPairFixture } from './fixtures/key-pair.fixture'; 8 | import { DIDService } from '../did/did.service'; 9 | import { KeyService } from '../key/key.service'; 10 | 11 | @Injectable() 12 | export class SeederService { 13 | private readonly logger = new Logger(SeederService.name, { timestamp: true }); 14 | 15 | constructor(private readonly keyService: KeyService, private readonly didService: DIDService) {} 16 | 17 | async seed() { 18 | this.logger.debug('seeding database'); 19 | 20 | for (const keyPair of keyPairFixture) { 21 | const key = await this.keyService.importKey(keyPair); 22 | const seededDid = await this.didService.registerKeyDID(key.keyId); 23 | this.logger.debug(`seeded ${seededDid?.id}`); 24 | } 25 | 26 | this.logger.debug('seeding database complete'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /apps/vc-api/src/setup.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { INestApplication, ValidationPipe, VersioningType } from '@nestjs/common'; 7 | import { NestFactory } from '@nestjs/core'; 8 | import { DocumentBuilder, OpenAPIObject, SwaggerModule } from '@nestjs/swagger'; 9 | import { AppModule } from './app.module'; 10 | import { TransactionEntityExceptionFilter } from './exception-filters/transaction-entity-exception.filter'; 11 | 12 | export const API_DEFAULT_VERSION = '1'; 13 | export const API_DEFAULT_VERSION_PREFIX = `/v${API_DEFAULT_VERSION}`; 14 | 15 | async function setupApp(): Promise { 16 | const app = await NestFactory.create(AppModule); 17 | app.enableCors({ origin: true }); 18 | app.useGlobalPipes(new ValidationPipe()); 19 | app.useGlobalFilters(new TransactionEntityExceptionFilter(app.getHttpAdapter())); 20 | app.enableVersioning({ 21 | type: VersioningType.URI, 22 | defaultVersion: [API_DEFAULT_VERSION] 23 | }); 24 | app.enableShutdownHooks(); 25 | return app; 26 | } 27 | 28 | function setupSwaggerDocument(app: INestApplication): OpenAPIObject { 29 | const config = new DocumentBuilder() 30 | .setTitle('VC-API') 31 | .setDescription('Sample VC-API') 32 | .setVersion('0.1') 33 | .build(); 34 | const document = SwaggerModule.createDocument(app, config); 35 | return document; 36 | } 37 | 38 | export { setupApp, setupSwaggerDocument }; 39 | -------------------------------------------------------------------------------- /apps/vc-api/src/utils/crypto.utils.ts: -------------------------------------------------------------------------------- 1 | import { TypedArrayEncoder } from '@credo-ts/core'; 2 | 3 | export const Base64ToBase58 = (base64: string): string => { 4 | return TypedArrayEncoder.toBase58(TypedArrayEncoder.fromBase64(base64)); 5 | }; 6 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/authenticate.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsString, ValidateNested } from 'class-validator'; 7 | import { ProvePresentationOptionsDto } from './prove-presentation-options.dto'; 8 | import { Type } from 'class-transformer'; 9 | import { ApiProperty } from '@nestjs/swagger'; 10 | 11 | /** 12 | * DTO which contains DID holder to authenticate and options 13 | */ 14 | export class AuthenticateDto { 15 | @IsString() 16 | @ApiProperty() 17 | did: string; 18 | 19 | @ValidateNested() 20 | @Type(() => ProvePresentationOptionsDto) 21 | @ApiProperty() 22 | options: ProvePresentationOptionsDto; 23 | } 24 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/credential.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { 7 | ArrayNotEmpty, 8 | IsArray, 9 | IsDateString, 10 | IsNotEmpty, 11 | IsObject, 12 | IsOptional, 13 | IsString 14 | } from 'class-validator'; 15 | import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger'; 16 | import { IsIssuer } from '../../exchanges/dtos/custom-validators'; 17 | import { IssuerDto } from './issuer.dto'; 18 | 19 | /** 20 | * A JSON-LD Verifiable Credential without a proof. 21 | * https://w3c-ccg.github.io/vc-api/issuer.html#operation/issueCredential 22 | */ 23 | @ApiExtraModels(IssuerDto) 24 | export class CredentialDto { 25 | [key: string]: unknown; 26 | 27 | @IsArray() 28 | @ApiProperty({ 29 | description: 'The JSON-LD context of the credential.', 30 | type: 'array', 31 | items: { oneOf: [{ type: 'string' }, { type: 'object' }] } 32 | }) 33 | '@context': Array>; 34 | 35 | @IsString() 36 | @IsOptional() 37 | @ApiPropertyOptional({ description: 'The ID of the credential.' }) 38 | id?: string; 39 | 40 | @IsArray() 41 | @ArrayNotEmpty() 42 | @IsString({ each: true }) 43 | @ApiProperty({ description: 'The JSON-LD type of the credential.' }) 44 | type: string[]; 45 | 46 | @IsIssuer() 47 | @ApiProperty({ 48 | description: 'A JSON-LD Verifiable Credential Issuer.', 49 | oneOf: [{ $ref: getSchemaPath(IssuerDto) }, { type: 'string' }] 50 | }) 51 | issuer: string | IssuerDto; 52 | 53 | @IsDateString() 54 | @ApiProperty({ description: 'The issuanceDate', example: '2022-09-21T11:49:03.205Z' }) 55 | issuanceDate: string; 56 | 57 | @IsString() 58 | @IsOptional() 59 | @ApiPropertyOptional({ description: 'The expirationDate', example: '2023-09-21T11:49:03.205Z' }) 60 | expirationDate?: string; 61 | 62 | @IsObject() 63 | @ApiProperty({ description: 'The subject' }) 64 | credentialSubject: Record; 65 | } 66 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/custom-class-validator/is-string-or-string-array.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { registerDecorator, ValidationOptions } from 'class-validator'; 7 | 8 | /** 9 | * A custom validator to check if a value is a string or a array or strings 10 | * https://github.com/typestack/class-validator#custom-validation-classes 11 | * https://github.com/typestack/class-validator/issues/160 12 | * https://github.com/typestack/class-validator/issues/558 13 | * An example use case of this is the "type" property from the VC data model: https://www.w3.org/TR/vc-data-model/#types 14 | * @param validationOptions 15 | * @returns 16 | */ 17 | export function IsStringOrStringArray(validationOptions?: ValidationOptions) { 18 | return function (object: unknown, propertyName: string) { 19 | registerDecorator({ 20 | name: 'isStringOrStringArray', 21 | target: object.constructor, 22 | propertyName: propertyName, 23 | constraints: [], 24 | options: validationOptions, 25 | validator: { 26 | validate(value: unknown) { 27 | return typeof value === 'string' || Array.isArray(value); 28 | } 29 | } 30 | }); 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/issue-credential.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsNotEmpty, IsNotEmptyObject, IsObject, IsOptional, ValidateNested } from 'class-validator'; 7 | import { CredentialDto } from './credential.dto'; 8 | import { IssueOptionsDto } from './issue-options.dto'; 9 | import { Type } from 'class-transformer'; 10 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 11 | 12 | /** 13 | * DTO which contains credential and options 14 | */ 15 | export class IssueCredentialDto { 16 | @ValidateNested() 17 | @Type(() => CredentialDto) 18 | @IsNotEmpty() 19 | @IsNotEmptyObject() 20 | @ApiProperty({ type: CredentialDto }) 21 | credential: CredentialDto; 22 | 23 | @Type(() => IssueOptionsDto) 24 | @ValidateNested() 25 | @IsOptional() 26 | @IsObject() 27 | @ApiPropertyOptional({ type: IssueOptionsDto }) 28 | options?: IssueOptionsDto; 29 | } 30 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/issue-options.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsObject, IsOptional, IsString } from 'class-validator'; 7 | import { ApiPropertyOptional } from '@nestjs/swagger'; 8 | 9 | /** 10 | * Options for specifying how the Data Integrity Proof is created for a credential issuance 11 | * https://w3c-ccg.github.io/vc-api/issuer.html#operation/issueCredential 12 | */ 13 | export class IssueOptionsDto { 14 | @IsString() 15 | @IsOptional() 16 | @ApiPropertyOptional({ 17 | description: 18 | 'The date and time of the proof (with a maximum accuracy in seconds). Default current system time.' 19 | }) 20 | created?: string; 21 | 22 | @IsString() 23 | @IsOptional() 24 | @ApiPropertyOptional({ 25 | description: 26 | 'A challenge provided by the requesting party of the proof. For example 6e62f66e-67de-11eb-b490-ef3eeefa55f2' 27 | }) 28 | challenge?: string; 29 | 30 | @IsString() 31 | @IsOptional() 32 | @ApiPropertyOptional({ 33 | description: 'The intended domain of validity for the proof. For example website.example' 34 | }) 35 | domain?: string; 36 | 37 | @IsObject() 38 | @IsOptional() 39 | @ApiPropertyOptional({ 40 | description: 41 | 'The method of credential status to issue the credential including. If omitted credential status will be included.' 42 | }) 43 | credentialStatus?: Record; 44 | } 45 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/issuer.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ApiProperty } from '@nestjs/swagger'; 7 | import { IsString } from 'class-validator'; 8 | 9 | export class IssuerDto { 10 | @IsString() 11 | @ApiProperty() 12 | id: string; 13 | 14 | constructor(properties: Partial) { 15 | Object.assign(this, properties); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/presentation.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsArray, IsOptional, IsString, ValidateNested } from 'class-validator'; 7 | import { VerifiableCredentialDto } from './verifiable-credential.dto'; 8 | import { IsStringOrStringArray } from './custom-class-validator/is-string-or-string-array'; 9 | import { Presentation } from '../../exchanges/types/presentation'; 10 | import { Type } from 'class-transformer'; 11 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 12 | 13 | /** 14 | * A JSON-LD Verifiable Presentation without a proof. 15 | * https://w3c-ccg.github.io/vc-api/holder.html#operation/provePresentation 16 | */ 17 | export class PresentationDto implements Presentation { 18 | @IsArray() 19 | @ApiProperty({ 20 | description: 'The JSON-LD context of the presentation.', 21 | type: 'array', 22 | items: { oneOf: [{ type: 'string' }, { type: 'object' }] } 23 | }) 24 | '@context': Array>; 25 | 26 | @IsString() 27 | @IsOptional() 28 | @ApiPropertyOptional({ 29 | description: 30 | 'The ID of the presentation. ' + 31 | 'The id property is optional and MAY be used to provide a unique identifier for the presentation. ' + 32 | 'https://www.w3.org/TR/vc-data-model/#presentations-0' 33 | }) 34 | id?: string; 35 | 36 | @IsStringOrStringArray() 37 | @ApiProperty({ description: 'The JSON-LD type of the presentation.' }) 38 | type: string[]; 39 | 40 | @IsString() 41 | @IsOptional() 42 | @ApiPropertyOptional({ 43 | description: 44 | 'The holder - will be ignored if no proof is present since there is no proof of authority over the credentials' 45 | }) 46 | holder?: string; 47 | 48 | @ValidateNested({ each: true }) 49 | @Type(() => VerifiableCredentialDto) 50 | @IsArray() 51 | @IsOptional() 52 | @ApiPropertyOptional({ 53 | description: 'The Verifiable Credentials', 54 | type: VerifiableCredentialDto, 55 | isArray: true 56 | }) 57 | verifiableCredential?: VerifiableCredentialDto[]; 58 | } 59 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/prove-presentation-options.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ProofPurpose } from '@sphereon/pex'; 7 | import { IsEnum, IsObject, IsOptional, IsString } from 'class-validator'; 8 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 9 | 10 | /** 11 | * Options for specifying how the Data Integrity Proof is created for a credential presentation proof 12 | * https://w3c-ccg.github.io/vc-api/#prove-presentation 13 | */ 14 | export class ProvePresentationOptionsDto { 15 | @IsString() 16 | @IsOptional() 17 | @ApiPropertyOptional({ 18 | description: 19 | 'The type of the proof. Default is an appropriate proof type corresponding to the verification method.' 20 | }) 21 | type?: string; 22 | 23 | @IsString() 24 | @ApiProperty({ 25 | description: 'The URI of the verificationMethod used for the proof. Default assertionMethod URI.' 26 | }) 27 | verificationMethod: string; 28 | 29 | @IsString() 30 | @IsOptional() 31 | @IsEnum(ProofPurpose) 32 | @ApiPropertyOptional({ 33 | description: "The purpose of the proof. Default 'assertionMethod'.", 34 | enum: ProofPurpose, 35 | enumName: 'ProofPurpose' 36 | }) 37 | proofPurpose?: ProofPurpose; 38 | 39 | @IsString() 40 | @IsOptional() 41 | @ApiPropertyOptional({ 42 | description: 43 | 'The date and time of the proof (with a maximum accuracy in seconds). Default current system time.' 44 | }) 45 | created?: string; 46 | 47 | @IsString() 48 | @ApiPropertyOptional({ 49 | description: 50 | 'A challenge provided by the requesting party of the proof. For example 6e62f66e-67de-11eb-b490-ef3eeefa55f2' 51 | }) 52 | challenge: string; 53 | 54 | @IsString() 55 | @IsOptional() 56 | @ApiPropertyOptional({ 57 | description: 'The intended domain of validity for the proof. For example website.example' 58 | }) 59 | domain?: string; 60 | 61 | @IsObject() 62 | @IsOptional() 63 | @ApiPropertyOptional({ 64 | description: 65 | 'The method of credential status to issue the credential including. If omitted credential status will be included.' 66 | }) 67 | credentialStatus?: Record; 68 | } 69 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/prove-presentation.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ValidateNested } from 'class-validator'; 7 | import { PresentationDto } from './presentation.dto'; 8 | import { ProvePresentationOptionsDto } from './prove-presentation-options.dto'; 9 | import { Type } from 'class-transformer'; 10 | import { ApiProperty } from '@nestjs/swagger'; 11 | 12 | /** 13 | * DTO which contains presentation and options 14 | */ 15 | export class ProvePresentationDto { 16 | @ValidateNested() 17 | @Type(() => PresentationDto) 18 | @ApiProperty() 19 | presentation: PresentationDto; 20 | 21 | @ValidateNested() 22 | @Type(() => ProvePresentationOptionsDto) 23 | @ApiProperty() 24 | options: ProvePresentationOptionsDto; 25 | } 26 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/verifiable-credential.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsObject } from 'class-validator'; 7 | import { CredentialDto } from './credential.dto'; 8 | import { ApiProperty } from '@nestjs/swagger'; 9 | 10 | /** 11 | * A JSON-LD Verifiable Credential with a proof. 12 | * https://w3c-ccg.github.io/vc-api/issuer.html#operation/issueCredential 13 | */ 14 | export class VerifiableCredentialDto extends CredentialDto { 15 | @IsObject() 16 | @ApiProperty({ description: 'A JSON-LD Linked Data proof.' }) 17 | proof: Record; 18 | } 19 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/verifiable-presentation.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsObject } from 'class-validator'; 7 | import { PresentationDto } from './presentation.dto'; 8 | import { ApiProperty } from '@nestjs/swagger'; 9 | 10 | /** 11 | * A JSON-LD Verifiable Presentation with a proof. 12 | * https://w3c-ccg.github.io/vc-api/verifier.html#operation/verifyPresentation 13 | */ 14 | export class VerifiablePresentationDto extends PresentationDto { 15 | @IsObject() 16 | @ApiProperty({ description: 'A JSON-LD Linked Data proof.' }) 17 | proof: Record & { 18 | challenge?: string; 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/verification-result.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsArray } from 'class-validator'; 7 | import { VerificationResult } from '../types/verification-result'; 8 | import { ApiProperty } from '@nestjs/swagger'; 9 | import { ProblemDetail } from '../types/problem-detail'; 10 | 11 | /** 12 | * A response object from verification of a credential or a presentation. 13 | * https://w3c-ccg.github.io/vc-api/verifier.html 14 | */ 15 | export class VerificationResultDto implements VerificationResult { 16 | @IsArray() 17 | @ApiProperty({ description: 'Warnings', deprecated: true }) 18 | warnings?: string[]; 19 | 20 | @IsArray() 21 | @ApiProperty({ description: 'Errors', deprecated: true }) 22 | errors?: string[]; 23 | 24 | @IsArray() 25 | @ApiProperty({ description: 'Problem Details' }) 26 | problemDetails: ProblemDetail[]; 27 | 28 | @ApiProperty({ 29 | description: `Overall verification assertion of the VerifiableCredential. 30 | This is set to True if no errors were detected during the verification process; otherwise, False.` 31 | }) 32 | verified: boolean; 33 | } 34 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/verify-credential.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerifiableCredentialDto } from './verifiable-credential.dto'; 7 | import { IsObject, ValidateNested, IsDefined } from 'class-validator'; 8 | import { Type } from 'class-transformer'; 9 | import { ApiProperty } from '@nestjs/swagger'; 10 | 11 | export class VerifyCredentialDto { 12 | /** 13 | * A JSON-LD Verifiable Credential with a proof. 14 | * https://w3c-ccg.github.io/vc-api/issuer.html#operation/issueCredential 15 | */ 16 | @IsObject() 17 | @IsDefined() 18 | @ValidateNested() 19 | @Type(() => VerifiableCredentialDto) 20 | @ApiProperty({ 21 | description: 22 | 'A JSON-LD Verifiable Credential with a proof. ' + 23 | 'https://w3c-ccg.github.io/vc-api/issuer.html#operation/issueCredential' 24 | }) 25 | verifiableCredential: VerifiableCredentialDto; 26 | } 27 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/verify-options.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ProofPurpose } from '@sphereon/pex'; 7 | import { IsOptional, IsString } from 'class-validator'; 8 | import { VerifyOptions } from '../types/verify-options'; 9 | import { ApiPropertyOptional } from '@nestjs/swagger'; 10 | 11 | /** 12 | * Parameters for verifying a verifiable credential or a verifiable presentation 13 | * https://w3c-ccg.github.io/vc-api/verifier.html#operation/verifyCredential 14 | * https://w3c-ccg.github.io/vc-api/verifier.html#operation/verifyPresentation 15 | */ 16 | export class VerifyOptionsDto implements VerifyOptions { 17 | @IsString() 18 | @IsOptional() 19 | @ApiPropertyOptional({ 20 | description: 'The URI of the verificationMethod used for the proof. Default assertionMethod URI.' 21 | }) 22 | verificationMethod?: string; 23 | 24 | @IsString() 25 | @IsOptional() 26 | @ApiPropertyOptional({ 27 | description: "The purpose of the proof. Default 'assertionMethod'.", 28 | enum: ProofPurpose, 29 | enumName: 'ProofPurpose' 30 | }) 31 | proofPurpose?: ProofPurpose; 32 | 33 | @IsString() 34 | @IsOptional() 35 | @ApiPropertyOptional({ 36 | description: 37 | 'The date and time of the proof (with a maximum accuracy in seconds). Default current system time.' 38 | }) 39 | created?: string; 40 | 41 | @IsString() 42 | @IsOptional() 43 | @ApiPropertyOptional({ 44 | description: 45 | 'A challenge provided by the requesting party of the proof. For example 6e62f66e-67de-11eb-b490-ef3eeefa55f2' 46 | }) 47 | challenge: string; 48 | 49 | @IsString() 50 | @IsOptional() 51 | @ApiPropertyOptional({ 52 | description: 'The intended domain of validity for the proof. For example website.example' 53 | }) 54 | domain?: string; 55 | } 56 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/dtos/verify-presentation.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerifyOptionsDto } from './verify-options.dto'; 7 | import { IsDefined, IsObject, ValidateNested } from 'class-validator'; 8 | import { Type } from 'class-transformer'; 9 | import { VerifiablePresentationDto } from './verifiable-presentation.dto'; 10 | import { ApiProperty } from '@nestjs/swagger'; 11 | 12 | export class VerifyPresentationDto { 13 | @IsObject() 14 | @IsDefined() 15 | @ValidateNested() 16 | @Type(() => VerifiablePresentationDto) 17 | @ApiProperty({ 18 | description: 19 | 'A JSON-LD Verifiable Credential with a proof. ' + 20 | 'https://w3c-ccg.github.io/vc-api/issuer.html#operation/issueCredential' 21 | }) 22 | verifiablePresentation: VerifiablePresentationDto; 23 | 24 | @IsObject() 25 | @IsDefined() 26 | @ValidateNested() 27 | @Type(() => VerifyOptionsDto) 28 | @ApiProperty({ 29 | description: 30 | 'Parameters for verifying a verifiable credential or a verifiable presentation ' + 31 | 'https://w3c-ccg.github.io/vc-api/verifier.html#operation/verifyCredential ' + 32 | 'https://w3c-ccg.github.io/vc-api/verifier.html#operation/verifyPresentation' 33 | }) 34 | options: VerifyOptionsDto; 35 | } 36 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/types/credential-verifier.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerifiableCredential } from '../../exchanges/types/verifiable-credential'; 7 | import { VerificationResult } from './verification-result'; 8 | import { VerifyOptions } from './verify-options'; 9 | 10 | export interface CredentialVerifier { 11 | verifyCredential: (vc: VerifiableCredential, options: VerifyOptions) => Promise; 12 | } 13 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/types/presentation-verifier.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerifiablePresentation } from '../../exchanges/types/verifiable-presentation'; 7 | import { VerificationResult } from './verification-result'; 8 | import { VerifyOptions } from './verify-options'; 9 | 10 | export interface PresentationVerifier { 11 | verifyPresentation: (vp: VerifiablePresentation, options: VerifyOptions) => Promise; 12 | } 13 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/types/problem-detail.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | export interface ProblemDetail { 7 | /** 8 | * Title 9 | */ 10 | title: string; 11 | 12 | /** 13 | * Type 14 | */ 15 | type?: string; 16 | 17 | /** 18 | * Status 19 | */ 20 | status?: string; 21 | 22 | /** 23 | * Detail 24 | */ 25 | detail?: string; 26 | 27 | /** 28 | * Instance 29 | */ 30 | instance?: any; 31 | } 32 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/types/verification-result.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ProblemDetail } from './problem-detail'; 7 | 8 | /** 9 | * A response object from verification of a credential or a presentation. 10 | * https://w3c-ccg.github.io/vc-api/verifier.html 11 | */ 12 | export interface VerificationResult { 13 | /** 14 | * Warnings 15 | * Deprecated: replaced by problem details 16 | */ 17 | warnings?: string[]; 18 | 19 | /** 20 | * Errors 21 | * Deprecated: replaced by problem details 22 | */ 23 | errors?: string[]; 24 | 25 | /** 26 | * Problem details 27 | */ 28 | problemDetails: ProblemDetail[]; 29 | 30 | /** 31 | * Verification status 32 | */ 33 | verified: boolean; 34 | } 35 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/types/verify-options.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * See "VerifyOptions" from 8 | * - https://w3c-ccg.github.io/vc-api/verifier.html#operation/verifyCredential 9 | * - https://w3c-ccg.github.io/vc-api/verifier.html#operation/verifyPresentation 10 | */ 11 | export interface VerifyOptions { 12 | challenge: string; 13 | proofPurpose?: string; 14 | } 15 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/credentials/utils/verification-result-transformer.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { W3cVerifyCredentialResult, W3cVerifyPresentationResult } from '@credo-ts/core'; 7 | import { VerificationResultDto } from '../dtos/verification-result.dto'; 8 | 9 | /** 10 | * This function reduces repeatability of code when tranforming the verification result 11 | */ 12 | export function transformVerificationResult( 13 | verificationResult: W3cVerifyCredentialResult | W3cVerifyPresentationResult 14 | ): VerificationResultDto { 15 | const errors = verificationResult.isValid ? [] : verificationResult.error['errors'].map((e) => e.message); 16 | const warnings = []; 17 | return { 18 | verified: verificationResult.isValid, 19 | errors, 20 | warnings, 21 | problemDetails: [ 22 | ...errors.map((e) => { 23 | return { title: e }; 24 | }), 25 | ...warnings 26 | ] 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/callback-configuration.dto.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { validate } from 'class-validator'; 7 | import { CallbackConfigurationDto } from './callback-configuration.dto'; 8 | 9 | describe('CallbackConfigurationDto', () => { 10 | it('should allow a URL that is a localhost', async () => { 11 | const dto = new CallbackConfigurationDto(); 12 | dto.url = 'http://localhost:80'; 13 | const result = await validate(dto); 14 | expect(result).toHaveLength(0); 15 | }); 16 | it('should allow a URL that is a toplevel domain', async () => { 17 | const dto = new CallbackConfigurationDto(); 18 | dto.url = 'https://example.com/callback'; 19 | const result = await validate(dto); 20 | expect(result).toHaveLength(0); 21 | }); 22 | it('should fail for URL that is not a URL', async () => { 23 | const dto = new CallbackConfigurationDto(); 24 | dto.url = 'not-a-url'; 25 | const result = await validate(dto); 26 | expect(result).toHaveLength(1); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/callback-configuration.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsUrl } from 'class-validator'; 7 | import { CallbackConfiguration } from '../types/callback-configuration'; 8 | import { ApiProperty } from '@nestjs/swagger'; 9 | 10 | /** 11 | * An exchange result callback 12 | */ 13 | export class CallbackConfigurationDto implements CallbackConfiguration { 14 | @IsUrl({ 15 | require_tld: false, //to allow localhost, see https://github.com/validatorjs/validator.js/issues/754 16 | require_protocol: true 17 | }) 18 | @ApiProperty({ 19 | description: 20 | 'URL at a callback notification will be sent:\n' + 21 | '- when a VP is submitted to a "mediated" exchange\n' + 22 | '- after the exchange has completed', 23 | example: 'https://example.com' 24 | }) 25 | url: string; 26 | } 27 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/callback.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { instanceToPlain, plainToInstance, Type } from 'class-transformer'; 7 | import { IsOptional, IsString, ValidateNested } from 'class-validator'; 8 | import { TransactionEntity } from '../entities/transaction.entity'; 9 | import { PresentationSubmissionFullDto } from './presentation-submission-full.dto'; 10 | import { VpRequestDto } from './vp-request.dto'; 11 | 12 | export class CallbackDto { 13 | /** 14 | * An id for the transaction 15 | */ 16 | @IsString() 17 | transactionId: string; 18 | 19 | /** 20 | * Each transaction is a part of an exchange 21 | * https://w3c-ccg.github.io/vc-api/#exchange-examples 22 | */ 23 | @IsString() 24 | exchangeId: string; 25 | 26 | /** 27 | * https://w3c-ccg.github.io/vp-request-spec/ 28 | */ 29 | @ValidateNested() 30 | @Type(() => VpRequestDto) 31 | vpRequest: VpRequestDto; 32 | 33 | /** 34 | * The submission to the VP Request 35 | * Is optional because submission may not have occured yet 36 | */ 37 | @ValidateNested() 38 | @Type(() => PresentationSubmissionFullDto) 39 | @IsOptional() 40 | presentationSubmission?: PresentationSubmissionFullDto; 41 | 42 | // TODO: make generic so that it can be used in all Dtos 43 | static toDto(transaction: TransactionEntity): CallbackDto { 44 | return plainToInstance(CallbackDto, instanceToPlain(transaction)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/custom-validators/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | export * from './issuer.validator'; 7 | export * from './presentation-definition-credential-query.validator'; 8 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/custom-validators/issuer.validator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { 7 | isObject, 8 | isString, 9 | registerDecorator, 10 | ValidationArguments, 11 | ValidationOptions, 12 | ValidatorConstraint, 13 | ValidatorConstraintInterface, 14 | validate 15 | } from 'class-validator'; 16 | import { IssuerDto } from '../../../credentials/dtos/issuer.dto'; 17 | 18 | @ValidatorConstraint({ async: true }) 19 | export class IsIssuerValidatorConstraint implements ValidatorConstraintInterface { 20 | async validate(value: unknown): Promise { 21 | if (isString(value)) { 22 | return true; 23 | } 24 | 25 | if (isObject(value)) { 26 | const instance = new IssuerDto(value); 27 | const validationErrors = await validate(instance); 28 | // TODO: check how to pass detailed message to the http response 29 | return validationErrors.length === 0; 30 | } 31 | 32 | return false; 33 | } 34 | 35 | defaultMessage(validationArguments?: ValidationArguments): string { 36 | return `${validationArguments.property} is not valid issuer`; 37 | } 38 | } 39 | 40 | export function IsIssuer(options?: ValidationOptions) { 41 | return function (object: unknown, propertyName: string) { 42 | registerDecorator({ 43 | target: object.constructor, 44 | propertyName, 45 | options, 46 | validator: IsIssuerValidatorConstraint 47 | }); 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/custom-validators/presentation-definition-credential-query.validator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IPresentationDefinition, PEX, Status } from '@sphereon/pex'; 7 | import { 8 | registerDecorator, 9 | ValidationArguments, 10 | ValidationOptions, 11 | ValidatorConstraint, 12 | ValidatorConstraintInterface 13 | } from 'class-validator'; 14 | 15 | /** 16 | * https://github.com/typestack/class-validator#custom-validation-decorators 17 | */ 18 | 19 | @ValidatorConstraint({ async: false }) 20 | export class IsPresentationDefinitionCredentialQueryConstraint implements ValidatorConstraintInterface { 21 | validate(value: IPresentationDefinition): boolean { 22 | const pex = new PEX(); 23 | try { 24 | const validated = pex.validateDefinition(value); 25 | const resultArray = Array.isArray(validated) ? validated : [validated]; 26 | const statuses = resultArray.map((checked) => checked.status); 27 | if (statuses.includes(Status.ERROR) || statuses.includes(Status.WARN)) { 28 | return false; 29 | } 30 | return true; 31 | } catch { 32 | return false; 33 | } 34 | } 35 | 36 | defaultMessage(validationArguments?: ValidationArguments): string { 37 | return `${validationArguments.property} is not valid PresentationDefinitionCredentialQuery`; 38 | } 39 | } 40 | 41 | export function IsPresentationDefinitionCredentialQuery(validationOptions?: ValidationOptions) { 42 | return function (object: unknown, propertyName: string) { 43 | registerDecorator({ 44 | target: object.constructor, 45 | propertyName: propertyName, 46 | options: validationOptions, 47 | validator: IsPresentationDefinitionCredentialQueryConstraint 48 | }); 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/exchange-definition.dto.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { validate } from 'class-validator'; 7 | import 'reflect-metadata'; 8 | import { RebeamCpoNode } from '../../../../test/vc-api/exchanges/rebeam/rebeam-cpo-node'; 9 | import { ResidentCardIssuance } from '../../../../test/vc-api/exchanges/resident-card/resident-card-issuance.exchange'; 10 | import { ResidentCardPresentation } from '../../../../test/vc-api/exchanges/resident-card/resident-card-presentation.exchange'; 11 | 12 | const callback = 'https://example.com/endpoint'; 13 | 14 | describe('ExchangeDefinition', () => { 15 | describe('Rebeam Presentation', () => { 16 | it('should be a valid exchange definition', async () => { 17 | const exchange = new RebeamCpoNode(callback); 18 | const definition = exchange.getExchangeDefinition(); 19 | const result = await validate(definition); 20 | expect(result).toHaveLength(0); 21 | }); 22 | }); 23 | describe('Resident Card Presentation', () => { 24 | it('should be a valid exchange definition', async () => { 25 | const exchange = new ResidentCardPresentation(callback); 26 | const definition = exchange.getExchangeDefinition(); 27 | const result = await validate(definition); 28 | expect(result).toHaveLength(0); 29 | }); 30 | }); 31 | describe('Resident Card Issuance', () => { 32 | it('should be a valid exchange definition', async () => { 33 | const exchange = new ResidentCardIssuance(callback); 34 | const definition = exchange.getExchangeDefinition(); 35 | const result = await validate(definition); 36 | expect(result).toHaveLength(0); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/exchange-interact-service-definition.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsEnum } from 'class-validator'; 7 | import { VpRequestInteractServiceType } from '../types/vp-request-interact-service-type'; 8 | import { ApiProperty } from '@nestjs/swagger'; 9 | 10 | /** 11 | * A definition of an interact service to be used in a workflow 12 | */ 13 | export class ExchangeInteractServiceDefinitionDto { 14 | @IsEnum(VpRequestInteractServiceType) 15 | @ApiProperty({ 16 | description: 17 | 'The "type" of the interact service.\n' + 18 | 'See Verifiable Presentation Request [Interaction Types](https://w3c-ccg.github.io/vp-request-spec/#interaction-types) for background.', 19 | enum: VpRequestInteractServiceType, 20 | enumName: 'VpRequestInteractServiceType' 21 | }) 22 | type: VpRequestInteractServiceType; 23 | } 24 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/exchange-response.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsBoolean, IsOptional, ValidateNested } from 'class-validator'; 7 | import { VerifiablePresentationDto } from '../../credentials/dtos/verifiable-presentation.dto'; 8 | import { VpRequestDto } from './vp-request.dto'; 9 | import { Type } from 'class-transformer'; 10 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 11 | 12 | /** 13 | * Describes the possible contents of response to a start/continue exchange request 14 | */ 15 | export class ExchangeResponseDto { 16 | @ValidateNested() 17 | @Type(() => VpRequestDto) 18 | @IsOptional() 19 | @ApiPropertyOptional({ 20 | description: 21 | 'Verifiable Presentation Request.\n' + 22 | 'Should conform to VP-Request specification.\n' + 23 | 'Will be returned if a VP is required to obtain more information from requester\n' + 24 | 'May not be returned if no further information is required (for example, at the end of the workflow)' 25 | }) 26 | vpRequest?: VpRequestDto; 27 | 28 | @ValidateNested() 29 | @Type(() => VerifiablePresentationDto) 30 | @IsOptional() 31 | @ApiPropertyOptional({ description: 'If it is an issuance response, then a vp may be provided' }) 32 | vp?: VerifiablePresentationDto; 33 | 34 | @IsBoolean() 35 | @ApiProperty({ 36 | description: 'True if an exchange submission is currently being processed or reviewed asyncronously' 37 | }) 38 | processingInProgress: boolean; 39 | } 40 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/presentation-definition.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ApiProperty } from '@nestjs/swagger'; 7 | import { IsArray, IsString } from 'class-validator'; 8 | 9 | export class PresentationDefinitionDto { 10 | @IsString() 11 | @ApiProperty() 12 | id: string; 13 | 14 | @IsArray() 15 | @ApiProperty({ type: 'object', isArray: true }) 16 | // TODO: consider defining DTO and validations 17 | input_descriptors: Record[]; 18 | } 19 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/presentation-submission-full.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerificationResultDto } from '../../credentials/dtos/verification-result.dto'; 7 | import { IsNotEmpty, ValidateNested } from 'class-validator'; 8 | import { VerifiablePresentationDto } from '../../credentials/dtos/verifiable-presentation.dto'; 9 | import { Type } from 'class-transformer'; 10 | 11 | /** 12 | * Presentation Submission Full Dto. 13 | * The complete presentation submission, including the submitted Verifiable Presentation 14 | */ 15 | export class PresentationSubmissionFullDto { 16 | /** 17 | * The result of the verification of the submitted VP 18 | */ 19 | @IsNotEmpty() 20 | @ValidateNested() 21 | @Type(() => VerificationResultDto) 22 | verificationResult: VerificationResultDto; 23 | 24 | /** 25 | * The Verifiable Presentation submitted in response to the transaction's VP Request 26 | */ 27 | @IsNotEmpty() 28 | @ValidateNested() 29 | @Type(() => VerifiablePresentationDto) 30 | vp: VerifiablePresentationDto; 31 | } 32 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/presentation-submission-secure.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerificationResultDto } from '../../credentials/dtos/verification-result.dto'; 7 | import { ValidateNested } from 'class-validator'; 8 | import { Type } from 'class-transformer'; 9 | 10 | /** 11 | * Secure Presentation Submission Dto 12 | * A representation of a presentation submission which does not have personal data associated with the submission 13 | */ 14 | export class PresentationSubmissionSecureDto { 15 | /** 16 | * The result of the verification of the submitted VP 17 | */ 18 | @ValidateNested() 19 | @Type(() => VerificationResultDto) 20 | verificationResult: VerificationResultDto; 21 | } 22 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/submission-review.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Type } from 'class-transformer'; 7 | import { IsEnum, IsOptional, ValidateNested } from 'class-validator'; 8 | import { VerifiablePresentationDto } from '../../credentials/dtos/verifiable-presentation.dto'; 9 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 10 | 11 | export enum ReviewResult { 12 | approved = 'approved', 13 | rejected = 'rejected' 14 | } 15 | 16 | export class SubmissionReviewDto { 17 | @IsEnum(ReviewResult) 18 | @ApiProperty({ 19 | description: 'The judgement made by the reviewer', 20 | enum: ReviewResult, 21 | enumName: 'ReviewResult' 22 | }) 23 | result: ReviewResult; 24 | 25 | @ValidateNested() 26 | @IsOptional() 27 | @Type(() => VerifiablePresentationDto) 28 | @ApiPropertyOptional({ 29 | description: 'A reviewer may want to include credentials (wrapped in a VP) to the holder' 30 | }) 31 | vp?: VerifiablePresentationDto; 32 | } 33 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/transaction.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { classToPlain, plainToClass, Type } from 'class-transformer'; 7 | import { IsOptional, IsString, ValidateNested } from 'class-validator'; 8 | import { TransactionEntity } from '../entities/transaction.entity'; 9 | import { PresentationSubmissionSecureDto } from './presentation-submission-secure.dto'; 10 | import { VpRequestDto } from './vp-request.dto'; 11 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 12 | 13 | export class TransactionDto { 14 | @IsString() 15 | @ApiProperty({ description: 'An id for the transaction' }) 16 | transactionId: string; 17 | 18 | @IsString() 19 | @ApiProperty({ 20 | description: 21 | 'Each transaction is a part of an exchange\n' + 'https://w3c-ccg.github.io/vc-api/#exchange-examples' 22 | }) 23 | exchangeId: string; 24 | 25 | @ValidateNested() 26 | @Type(() => VpRequestDto) 27 | @ApiProperty({ description: 'https://w3c-ccg.github.io/vp-request-spec' }) 28 | vpRequest: VpRequestDto; 29 | 30 | @ValidateNested() 31 | @Type(() => PresentationSubmissionSecureDto) 32 | @IsOptional() 33 | @ApiPropertyOptional({ 34 | description: 'The submission to the VP Request\nIs optional because submission may not have occured yet' 35 | }) 36 | presentationSubmission?: PresentationSubmissionSecureDto; 37 | 38 | // TODO: make generic so that it can be used in all Dtos 39 | static toDto(transaction: TransactionEntity): TransactionDto { 40 | const data = classToPlain(transaction); 41 | return plainToClass(TransactionDto, data); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/vp-request-did-auth-query.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | export class VpRequestDidAuthQueryDto {} 7 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/vp-request-interact-service.dto.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { validate } from 'class-validator'; 7 | import { VpRequestInteractServiceType } from '../types/vp-request-interact-service-type'; 8 | import { VpRequestInteractServiceDto } from './vp-request-interact-service.dto'; 9 | 10 | describe('VpRequestInteractServiceDto', () => { 11 | it('should allow a serviceEndpoint that is a localhost', async () => { 12 | const dto = new VpRequestInteractServiceDto(); 13 | dto.serviceEndpoint = 'http://localhost:80'; 14 | dto.type = VpRequestInteractServiceType.unmediatedPresentation; 15 | const result = await validate(dto); 16 | expect(result).toHaveLength(0); 17 | }); 18 | it('should allow a serviceEndpoint that is a toplevel domain', async () => { 19 | const dto = new VpRequestInteractServiceDto(); 20 | dto.serviceEndpoint = 'https://example.com/callback'; 21 | dto.type = VpRequestInteractServiceType.unmediatedPresentation; 22 | const result = await validate(dto); 23 | expect(result).toHaveLength(0); 24 | }); 25 | it('should fail for serviceEndpoint that is not a URL', async () => { 26 | const dto = new VpRequestInteractServiceDto(); 27 | dto.serviceEndpoint = 'not-a-url'; 28 | dto.type = VpRequestInteractServiceType.unmediatedPresentation; 29 | const result = await validate(dto); 30 | expect(result).toHaveLength(1); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/vp-request-interact-service.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsEnum, IsUrl } from 'class-validator'; 7 | import { VpRequestInteractServiceType } from '../types/vp-request-interact-service-type'; 8 | import { ApiProperty } from '@nestjs/swagger'; 9 | 10 | /** 11 | * https://w3c-ccg.github.io/vp-request-spec/#interaction-types 12 | */ 13 | export class VpRequestInteractServiceDto { 14 | @IsEnum(VpRequestInteractServiceType) 15 | @ApiProperty({ 16 | enum: VpRequestInteractServiceType, 17 | enumName: 'VpRequestInteractServiceType' 18 | }) 19 | type: VpRequestInteractServiceType; 20 | 21 | @IsUrl({ 22 | require_tld: false, // to allow localhost, see https://github.com/validatorjs/validator.js/issues/754 23 | require_protocol: true 24 | }) 25 | @ApiProperty({ description: 'URL at which the credential exchange can be continued' }) 26 | serviceEndpoint: string; 27 | } 28 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/vp-request-interact.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Type } from 'class-transformer'; 7 | import { IsArray, ValidateNested } from 'class-validator'; 8 | import { VpRequestInteractServiceDto } from './vp-request-interact-service.dto'; 9 | import { ApiProperty } from '@nestjs/swagger'; 10 | 11 | /** 12 | * https://w3c-ccg.github.io/vp-request-spec/#interaction-types 13 | */ 14 | export class VpRequestInteractDto { 15 | @ValidateNested({ each: true }) 16 | @IsArray() 17 | @Type(() => VpRequestInteractServiceDto) 18 | @ApiProperty({ type: VpRequestInteractServiceDto, isArray: true }) 19 | service: VpRequestInteractServiceDto[]; 20 | } 21 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/vp-request-presentation-defintion-query.dto.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { validate } from 'class-validator'; 7 | import 'reflect-metadata'; 8 | import { VpRequestPresentationDefinitionQueryDto } from './vp-request-presentation-defintion-query.dto'; 9 | 10 | describe('VpRequestPresentationDefinitionQueryDto', () => { 11 | it('should validate valid presentation definition', async () => { 12 | const query = new VpRequestPresentationDefinitionQueryDto(); 13 | query.presentationDefinition = { 14 | id: '286bc1e0-f1bd-488a-a873-8d71be3c690e', 15 | input_descriptors: [ 16 | { 17 | id: 'my_pres_definition', 18 | name: 'My presentation definition' 19 | } 20 | ] 21 | }; 22 | const result = await validate(query); 23 | expect(result).toHaveLength(0); 24 | }); 25 | it('should validate invalid presentation definition', async () => { 26 | const query = new VpRequestPresentationDefinitionQueryDto(); 27 | query.presentationDefinition = { 28 | //@ts-expect-error: [justification is needed here] 29 | input_descriptors: 10 30 | }; 31 | const result = await validate(query); 32 | expect(result).toHaveLength(1); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/vp-request-presentation-defintion-query.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsPresentationDefinitionCredentialQuery } from './custom-validators'; 7 | import { ApiProperty } from '@nestjs/swagger'; 8 | import { IsNotEmptyObject, ValidateNested } from 'class-validator'; 9 | import { PresentationDefinitionDto } from './presentation-definition.dto'; 10 | import { Type } from 'class-transformer'; 11 | 12 | /** 13 | * https://github.com/w3c-ccg/vp-request-spec/issues/7#issuecomment-1067036904 14 | */ 15 | export class VpRequestPresentationDefinitionQueryDto { 16 | @ValidateNested() 17 | @IsPresentationDefinitionCredentialQuery() 18 | @IsNotEmptyObject() 19 | @Type(() => PresentationDefinitionDto) 20 | @ApiProperty({ 21 | description: 22 | 'An object conforming to the Presentation Definition specification\n' + 23 | 'https://github.com/w3c-ccg/vp-request-spec/issues/7#issuecomment-1067036904', 24 | type: PresentationDefinitionDto 25 | }) 26 | presentationDefinition: PresentationDefinitionDto; 27 | } 28 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/vp-request-query.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Type } from 'class-transformer'; 7 | import { ArrayMaxSize, ArrayMinSize, IsArray, IsEnum, ValidateNested } from 'class-validator'; 8 | import { VpRequestQueryType } from '../types/vp-request-query-type'; 9 | import { VpRequestDidAuthQueryDto } from './vp-request-did-auth-query.dto'; 10 | import { VpRequestPresentationDefinitionQueryDto } from './vp-request-presentation-defintion-query.dto'; 11 | import { ApiExtraModels, ApiProperty, getSchemaPath } from '@nestjs/swagger'; 12 | 13 | /** 14 | * https://w3c-ccg.github.io/vp-request-spec/#query-and-response-types 15 | */ 16 | @ApiExtraModels(VpRequestPresentationDefinitionQueryDto, VpRequestDidAuthQueryDto) 17 | export class VpRequestQueryDto { 18 | @IsEnum(VpRequestQueryType) 19 | @ApiProperty({ 20 | description: 21 | 'Query types as listed in the VP Request spec.\n' + 22 | 'https://w3c-ccg.github.io/vp-request-spec/#query-and-response-types\n\n' + 23 | 'The "PresentationDefinition" type is proposed here: https://github.com/w3c-ccg/vp-request-spec/issues/7', 24 | enum: VpRequestQueryType, 25 | enumName: 'VpRequestQueryType' 26 | }) 27 | type: VpRequestQueryType; 28 | 29 | /** 30 | * The credential query. 31 | * It should correspond to the query type. 32 | */ 33 | @ValidateNested({ each: true }) 34 | @IsArray() 35 | @ArrayMinSize(0) 36 | @ArrayMaxSize(1) 37 | @Type(() => VpRequestPresentationDefinitionQueryDto, { 38 | discriminator: { 39 | property: 'type', 40 | subTypes: [ 41 | { value: VpRequestPresentationDefinitionQueryDto, name: VpRequestQueryType.presentationDefinition }, 42 | { value: VpRequestDidAuthQueryDto, name: VpRequestQueryType.didAuth } 43 | ] 44 | } 45 | }) 46 | @ApiProperty({ 47 | description: 'The credential query.\nIt should correspond to the query type.', 48 | type: 'array', 49 | items: { 50 | oneOf: [ 51 | { $ref: getSchemaPath(VpRequestPresentationDefinitionQueryDto) }, 52 | { $ref: getSchemaPath(VpRequestDidAuthQueryDto) } 53 | ] 54 | }, 55 | example: [ 56 | { 57 | presentationDefinition: { 58 | id: '00000000-0000-4000-0000-000000000000', 59 | input_descriptors: [] 60 | } 61 | } 62 | ] 63 | }) 64 | credentialQuery: Array; 65 | } 66 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/dtos/vp-request.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { classToPlain, plainToClass, Type } from 'class-transformer'; 7 | import { IsArray, IsString, ValidateNested } from 'class-validator'; 8 | import { VpRequestEntity } from '../entities/vp-request.entity'; 9 | import { VpRequestInteractDto } from './vp-request-interact.dto'; 10 | import { VpRequestQueryDto } from './vp-request-query.dto'; 11 | import { ApiProperty } from '@nestjs/swagger'; 12 | 13 | /** 14 | * VP Request DTO 15 | * Should conform to https://w3c-ccg.github.io/vp-request-spec 16 | */ 17 | export class VpRequestDto { 18 | @IsString() 19 | @ApiProperty({ 20 | description: 21 | 'From https://w3c-ccg.github.io/vp-request-spec/#format :\n' + 22 | '"Challenge that will be digitally signed in the authentication proof\n' + 23 | 'that will be attached to the VerifiablePresentation response"' 24 | }) 25 | challenge: string; 26 | 27 | @IsArray() 28 | @ValidateNested({ each: true }) 29 | @Type(() => VpRequestQueryDto) 30 | @ApiProperty({ 31 | description: 32 | 'From https://w3c-ccg.github.io/vp-request-spec/#format :\n' + 33 | '"To make a request for one or more objects wrapped in a Verifiable Presentation,\n' + 34 | 'a client constructs a JSON request describing one or more queries that it wishes to perform from the receiver."\n' + 35 | '"The query type serves as the main extension point mechanism for requests for data in the presentation.\n' + 36 | 'This document defines several common query types."', 37 | type: VpRequestQueryDto, 38 | isArray: true 39 | }) 40 | query: VpRequestQueryDto[]; 41 | 42 | @ValidateNested() 43 | @Type(() => VpRequestInteractDto) 44 | @ApiProperty() 45 | interact: VpRequestInteractDto; 46 | 47 | static toDto(vpRequestEntity: VpRequestEntity): VpRequestDto { 48 | const data = classToPlain(vpRequestEntity); 49 | return plainToClass(VpRequestEntity, data); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/entities/exchange.entity.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ExchangeDefinitionDto } from '../dtos/exchange-definition.dto'; 7 | import { ExchangeEntity } from './exchange.entity'; 8 | 9 | describe('ExchangeEntity', () => { 10 | describe('start', () => { 11 | it('should respond with the same transaction id if it is a oneTime exchange', async () => { 12 | const exchangeDefinition: ExchangeDefinitionDto = { 13 | exchangeId: '9ec5686e-6381-41c4-9286-3c93cdefac53', 14 | interactServices: [], 15 | query: [], 16 | callback: [], 17 | isOneTime: true 18 | }; 19 | const exchange = new ExchangeEntity(exchangeDefinition); 20 | const transaction_1 = exchange.start(); 21 | const transaction_2 = exchange.start(); 22 | expect(transaction_1.transactionId).toEqual(transaction_2.transactionId); 23 | }); 24 | 25 | it('should respond with different transaction id if it is not a ontTime exchange', async () => { 26 | const exchangeDefinition: ExchangeDefinitionDto = { 27 | exchangeId: '9ec5686e-6381-41c4-9286-3c93cdefac53', 28 | interactServices: [], 29 | query: [], 30 | callback: [], 31 | isOneTime: false 32 | }; 33 | const exchange = new ExchangeEntity(exchangeDefinition); 34 | const transaction_1 = exchange.start(); 35 | const transaction_2 = exchange.start(); 36 | expect(transaction_1.transactionId).not.toEqual(transaction_2.transactionId); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/entities/presentation-review.entity.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Column, Entity } from 'typeorm'; 7 | import { PresentationReviewStatus } from '../types/presentation-review-status'; 8 | import { VerifiablePresentation } from '../types/verifiable-presentation'; 9 | 10 | /** 11 | * A TypeOrm entity representing a Presentation Review 12 | * 13 | * For a mediated presentation flow, there could be a review of the presentation 14 | */ 15 | @Entity() 16 | export class PresentationReviewEntity { 17 | /** 18 | * Id for this presentation review 19 | */ 20 | @Column('text', { primary: true }) 21 | presentationReviewId: string; 22 | 23 | @Column('text') 24 | reviewStatus: PresentationReviewStatus; 25 | 26 | /** 27 | * If issuing credentials as a part of this exchange, they could be returned in a VP 28 | */ 29 | @Column('simple-json', { nullable: true }) 30 | VP?: VerifiablePresentation; 31 | } 32 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/entities/presentation-submission.entity.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerificationResult } from '../../credentials/types/verification-result'; 7 | import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; 8 | import { VerifiablePresentation } from '../types/verifiable-presentation'; 9 | 10 | /** 11 | * A TypeOrm entity representing a Presentation Submission. 12 | * This is a presentation submitted in response to a VP Request https://w3c-ccg.github.io/vp-request-spec/. 13 | * Related to a presentation exchange submission (https://identity.foundation/presentation-exchange/#presentation-submission), 14 | * in that the submitted VP could contain a presentation_submission. 15 | */ 16 | @Entity() 17 | export class PresentationSubmissionEntity { 18 | constructor(vp: VerifiablePresentation, verificationResult: VerificationResult) { 19 | this.vpHolder = vp?.holder; 20 | this.verificationResult = verificationResult; 21 | } 22 | 23 | @PrimaryGeneratedColumn() 24 | id: string; 25 | 26 | /** 27 | * The result of the verification of the submitted VP 28 | */ 29 | @Column('simple-json') 30 | verificationResult: VerificationResult; 31 | 32 | @Column('text', { nullable: true }) 33 | vpHolder: string; 34 | } 35 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/entities/vp-request.entity.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Column, Entity } from 'typeorm'; 7 | import { VpRequestInteractService } from '../types/vp-request-interact-service'; 8 | import { VpRequestQuery } from '../types/vp-request-query'; 9 | 10 | /** 11 | * A TypeOrm entity representing a VP Request 12 | * Should conform to https://w3c-ccg.github.io/vp-request-spec 13 | */ 14 | @Entity() 15 | export class VpRequestEntity { 16 | /** 17 | * From https://w3c-ccg.github.io/vp-request-spec/#format : 18 | * "Challenge that will be digitally signed in the authentication proof 19 | * that will be attached to the VerifiablePresentation response" 20 | */ 21 | @Column('text', { primary: true }) 22 | challenge: string; 23 | 24 | @Column('simple-json') 25 | query: VpRequestQuery[]; 26 | 27 | /** 28 | * The schema for this property is taken from https://github.com/w3c-ccg/vc-api/issues/245 29 | */ 30 | @Column('simple-json') 31 | interact: { service: VpRequestInteractService[] }; 32 | } 33 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/types/callback-configuration.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * A callback to execute after an exchange 8 | */ 9 | export interface CallbackConfiguration { 10 | url: string; 11 | } 12 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/types/credential.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * A JSON-LD Verifiable Credential without a proof. 8 | * https://w3c-ccg.github.io/vc-api/issuer.html#operation/issueCredential 9 | */ 10 | export interface Credential { 11 | // Needed to match @sphereon/pex type 12 | // https://github.com/Sphereon-Opensource/pex/blob/a1f56d6baabf1b0e1e28a08d04ffc97d76863207/lib/types/SSI.types.ts#L87 13 | [x: string]: unknown; 14 | 15 | '@context': Array>; 16 | 17 | /** 18 | * The ID of the credential. 19 | */ 20 | id?: string; 21 | 22 | /** 23 | * The JSON-LD type of the credential. 24 | */ 25 | type: string[]; 26 | 27 | /** 28 | * A JSON-LD Verifiable Credential Issuer. 29 | */ 30 | issuer: string | { id: string }; 31 | 32 | /** 33 | * The issuanceDate 34 | */ 35 | issuanceDate: string; 36 | 37 | /** 38 | * The expirationDate 39 | */ 40 | expirationDate?: string; 41 | 42 | /** 43 | * The subject 44 | */ 45 | credentialSubject: Record; 46 | } 47 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/types/presentation-review-status.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * The status of a presentation review 8 | * These statuses are NON-STANDARD 9 | * 10 | * Similar to {@link https://github.com/energywebfoundation/ssi-hub/blob/8b860e7cdae4e1b1aa75afeab8b9df7ab26befbb/src/modules/claim/claim.types.ts#L7} 11 | * 12 | * Maybe similar to Aries Issue-Credential protocol {@link https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md} 13 | */ 14 | export enum PresentationReviewStatus { 15 | pendingSubmission = 'pending_submission', 16 | pendingReview = 'pending_review', 17 | approved = 'approved', 18 | rejected = 'rejected' 19 | } 20 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/types/presentation.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerifiableCredential } from './verifiable-credential'; 7 | 8 | /** 9 | * A JSON-LD Verifiable Presentation without a proof. 10 | * https://w3c-ccg.github.io/vc-api/holder.html#operation/provePresentation 11 | */ 12 | export interface Presentation { 13 | /** 14 | * The JSON-LD context of the presentation. 15 | */ 16 | '@context': Array>; 17 | 18 | /** 19 | * The ID of the presentation. 20 | * The id property is optional and MAY be used to provide a unique identifier for the presentation. 21 | * https://www.w3.org/TR/vc-data-model/#presentations-0 22 | */ 23 | id?: string; 24 | 25 | /** 26 | * The JSON-LD type of the presentation. 27 | */ 28 | type: string[]; 29 | 30 | /** 31 | * The holder - will be ignored if no proof is present since there is no proof of authority over the credentials 32 | */ 33 | holder?: string; 34 | 35 | /** 36 | * The Verifiable Credentials 37 | */ 38 | verifiableCredential?: VerifiableCredential[]; 39 | } 40 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/types/submission-verifier.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerificationResultDto } from '../../credentials/dtos/verification-result.dto'; 7 | import { VpRequestEntity } from '../entities/vp-request.entity'; 8 | import { VerifiablePresentation } from './verifiable-presentation'; 9 | 10 | /** 11 | * Intended to represent a verifier of a VP Request Submission. 12 | * TODO: Maybe shouldn't only be for VPR verification but allow for more generic types. 13 | */ 14 | export interface SubmissionVerifier { 15 | verifyVpRequestSubmission: ( 16 | vp: VerifiablePresentation, 17 | vpRequest: VpRequestEntity 18 | ) => Promise; 19 | } 20 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/types/verifiable-credential.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IProof } from '@sphereon/pex'; 7 | import { Credential } from './credential'; 8 | 9 | /** 10 | * A JSON-LD Verifiable Credential with a proof. 11 | * https://w3c-ccg.github.io/vc-api/issuer.html#operation/issueCredential 12 | */ 13 | export interface VerifiableCredential extends Credential { 14 | /** 15 | * A JSON-LD Linked Data proof. 16 | */ 17 | proof: Record | IProof; 18 | } 19 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/types/verifiable-presentation.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Presentation } from './presentation'; 7 | 8 | /** 9 | * A JSON-LD Verifiable Presentation with a proof. 10 | * https://w3c-ccg.github.io/vc-api/verifier.html#operation/verifyPresentation 11 | */ 12 | export interface VerifiablePresentation extends Presentation { 13 | /** 14 | * A JSON-LD Linked Data proof. 15 | */ 16 | proof: Record; 17 | } 18 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/types/vp-request-interact-service-definition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VpRequestInteractServiceType } from './vp-request-interact-service-type'; 7 | 8 | /** 9 | * A definition of an interact service to be used in an exchange 10 | */ 11 | export interface VpRequestInteractServiceDefinition { 12 | type: VpRequestInteractServiceType; 13 | } 14 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/types/vp-request-interact-service-type.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * The interact service types that are both 8 | * - supported by the wallet app 9 | * - listed in the VP Request spec https://w3c-ccg.github.io/vp-request-spec/#interaction-types 10 | */ 11 | export enum VpRequestInteractServiceType { 12 | /** 13 | * https://w3c-ccg.github.io/vp-request-spec/#unmediated-presentation 14 | */ 15 | unmediatedPresentation = 'UnmediatedHttpPresentationService2021', 16 | 17 | /** 18 | * See https://w3c-ccg.github.io/vp-request-spec/#mediated-presentation for background. 19 | * Note that the specification (as of v0.1, 25-04-2022), refers to "MediatedBrowserPresentationService2021". 20 | * This [GitHub issue](https://github.com/w3c-ccg/vp-request-spec/issues/17) is open to discuss the usage of Mediated Presentations Services 21 | */ 22 | mediatedPresentation = 'MediatedHttpPresentationService2021' 23 | } 24 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/types/vp-request-interact-service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VpRequestInteractServiceType } from './vp-request-interact-service-type'; 7 | 8 | export interface VpRequestInteractService { 9 | type: VpRequestInteractServiceType; 10 | serviceEndpoint: string; 11 | } 12 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/types/vp-request-query-type.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * Query types as listed in the VP Request spec. 8 | * https://w3c-ccg.github.io/vp-request-spec/#query-types 9 | * 10 | */ 11 | export enum VpRequestQueryType { 12 | /** 13 | * https://w3c-ccg.github.io/vp-request-spec/#did-authentication-request 14 | */ 15 | didAuth = 'DIDAuth', 16 | 17 | /** 18 | * A presentation definition https://identity.foundation/presentation-exchange/#presentation-definition 19 | * This type is proposed here: https://github.com/w3c-ccg/vp-request-spec/issues/7 20 | */ 21 | presentationDefinition = 'PresentationDefinition' 22 | } 23 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exchanges/types/vp-request-query.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VpRequestQueryType } from './vp-request-query-type'; 7 | 8 | /** 9 | * From https://w3c-ccg.github.io/vp-request-spec/#format : 10 | * "To make a request for one or more objects wrapped in a Verifiable Presentation, 11 | * a client constructs a JSON request describing one or more queries that it wishes to perform from the receiver." 12 | * "The query type serves as the main extension point mechanism for requests for data in the presentation. 13 | * This document defines several common query types." 14 | */ 15 | export interface VpRequestQuery { 16 | type: VpRequestQueryType; 17 | credentialQuery: any; 18 | } 19 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/exhaustiveMatchGuard.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * Guard that can be used for pattern matching 8 | * Used to ensure that all paths in a switch are exhaustive 9 | * See https://medium.com/technogise/type-safe-and-exhaustive-switch-statements-aka-pattern-matching-in-typescript-e3febd433a7a 10 | */ 11 | export const exhaustiveMatchGuard = (_: never): never => { 12 | throw new Error('Should not have reached here'); 13 | }; 14 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/vc-api.controller.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Test, TestingModule } from '@nestjs/testing'; 7 | import { VcApiController } from './vc-api.controller'; 8 | import { CredentialsService } from './credentials/credentials.service'; 9 | import { ExchangeService } from './exchanges/exchange.service'; 10 | import { WorkflowService } from './workflows/workflow.service'; 11 | 12 | describe('VcApiController', () => { 13 | let controller: VcApiController; 14 | 15 | beforeEach(async () => { 16 | const module: TestingModule = await Test.createTestingModule({ 17 | controllers: [VcApiController], 18 | providers: [ 19 | { 20 | provide: CredentialsService, 21 | useValue: {} 22 | }, 23 | { 24 | provide: ExchangeService, 25 | useValue: {} 26 | }, 27 | { 28 | provide: WorkflowService, 29 | useValue: {} 30 | } 31 | ] 32 | }).compile(); 33 | 34 | controller = module.get(VcApiController); 35 | }); 36 | 37 | it('should be defined', () => { 38 | expect(controller).toBeDefined(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/vc-api.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Module } from '@nestjs/common'; 7 | import { HttpModule } from '@nestjs/axios'; 8 | import { ConfigModule } from '@nestjs/config'; 9 | import { TypeOrmModule } from '@nestjs/typeorm'; 10 | import { DidModule } from '../did/did.module'; 11 | import { KeyModule } from '../key/key.module'; 12 | import { VcApiController } from './vc-api.controller'; 13 | import { CredentialsService } from './credentials/credentials.service'; 14 | import { ExchangeService } from './exchanges/exchange.service'; 15 | import { ExchangeEntity } from './exchanges/entities/exchange.entity'; 16 | import { VpRequestEntity } from './exchanges/entities/vp-request.entity'; 17 | import { TransactionEntity } from './exchanges/entities/transaction.entity'; 18 | import { PresentationReviewEntity } from './exchanges/entities/presentation-review.entity'; 19 | import { PresentationSubmissionEntity } from './exchanges/entities/presentation-submission.entity'; 20 | import { VpSubmissionVerifierService } from './exchanges/vp-submission-verifier.service'; 21 | import { VpSubmissionVerifierService as WfVpSubmissionVerifierService } from './workflows/vp-submission-verifier.service'; 22 | import { WorkflowService } from './workflows/workflow.service'; 23 | import { WorkflowEntity } from './workflows/entities/workflow.entity'; 24 | import { WfExchangeEntity } from './workflows/entities/wf-exchange.entity'; 25 | 26 | @Module({ 27 | imports: [ 28 | DidModule, 29 | KeyModule, 30 | TypeOrmModule.forFeature([ 31 | VpRequestEntity, 32 | ExchangeEntity, 33 | TransactionEntity, 34 | PresentationReviewEntity, 35 | PresentationSubmissionEntity, 36 | WorkflowEntity, 37 | WfExchangeEntity 38 | ]), 39 | ConfigModule, 40 | HttpModule 41 | ], 42 | controllers: [VcApiController], 43 | providers: [ 44 | CredentialsService, 45 | ExchangeService, 46 | VpSubmissionVerifierService, 47 | WorkflowService, 48 | WfVpSubmissionVerifierService 49 | ], 50 | exports: [CredentialsService, ExchangeService, WorkflowService] 51 | }) 52 | export class VcApiModule {} 53 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/callback-configuration.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsUrl } from 'class-validator'; 7 | import { CallbackConfiguration } from '../types/callback-configuration'; 8 | import { ApiProperty } from '@nestjs/swagger'; 9 | 10 | /** 11 | * An exchange result callback 12 | */ 13 | export class CallbackConfigurationDto implements CallbackConfiguration { 14 | @IsUrl({ 15 | require_tld: false, //to allow localhost, see https://github.com/validatorjs/validator.js/issues/754 16 | require_protocol: true 17 | }) 18 | @ApiProperty({ 19 | description: 20 | 'URL at a callback notification will be sent:\n' + 21 | '- when a VP is submitted to a "mediated" exchange\n' + 22 | '- after the exchange has completed', 23 | example: 'https://example.com' 24 | }) 25 | url: string; 26 | } 27 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/callback.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { instanceToPlain, plainToInstance, Type } from 'class-transformer'; 7 | import { IsOptional, IsString, ValidateNested } from 'class-validator'; 8 | import { PresentationSubmissionFullDto } from './presentation-submission-full.dto'; 9 | import { VpRequestDto } from './vp-request.dto'; 10 | import { QueryExchangeStep } from '../types/query-exchange-step'; 11 | import { IssuanceExchangeStep } from '../types/issuance-exchange-step'; 12 | 13 | export class CallbackDto { 14 | /** 15 | * An id for the current step 16 | */ 17 | @IsString() 18 | stepId: string; 19 | 20 | /** 21 | * Each step is a part of an exchange 22 | * https://w3c-ccg.github.io/vc-api/#exchange-examples 23 | */ 24 | @IsString() 25 | exchangeId: string; 26 | 27 | /** 28 | * https://w3c-ccg.github.io/vp-request-spec/ 29 | */ 30 | @ValidateNested() 31 | @Type(() => VpRequestDto) 32 | vpRequest: VpRequestDto; 33 | 34 | /** 35 | * The submission to the VP Request 36 | * Is optional because submission may not have occured yet 37 | */ 38 | @ValidateNested() 39 | @Type(() => PresentationSubmissionFullDto) 40 | @IsOptional() 41 | presentationSubmission?: PresentationSubmissionFullDto; 42 | 43 | // TODO: make generic so that it can be used in all Dtos 44 | static toDto(step: QueryExchangeStep | IssuanceExchangeStep): CallbackDto { 45 | return plainToInstance(CallbackDto, instanceToPlain(step)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/create-exchange-success.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString } from 'class-validator'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | 4 | /** 5 | * https://w3c-ccg.github.io/vc-api/#create-exchange 6 | */ 7 | export class CreateExchangeSuccessDto { 8 | @ApiProperty({ 9 | description: 'The URL that uniquely identifies the exchange.' 10 | }) 11 | @IsString() 12 | exchangeId: string; 13 | 14 | // TODO: add other properties, in particular "state" 15 | 16 | @ApiProperty({ 17 | description: 'The semantic string ID for the current step.' 18 | }) 19 | @IsString() 20 | step: string; 21 | 22 | @ApiProperty({ 23 | description: 'Status of current exchange step' 24 | }) 25 | @IsString() 26 | state: string; 27 | } 28 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/create-exchange.dto.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * https://w3c-ccg.github.io/vc-api/#create-exchange 3 | */ 4 | export class CreateExchangeDto { 5 | // TODO: consider add ttl and variables in the future 6 | } 7 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/create-workflow-request.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, ValidateNested, IsOptional } from 'class-validator'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | import { Type } from 'class-transformer'; 4 | import { WorkflowStepDefinitionDto } from './workflow-step-definition.dto'; 5 | 6 | export class StepDefinitions { 7 | [key: string]: WorkflowStepDefinitionDto; 8 | } 9 | 10 | export class WorkflowConfigDto { 11 | @ApiProperty({ 12 | description: 'One or more steps required to complete an exchange on the workflow.', 13 | type: StepDefinitions 14 | }) 15 | @ValidateNested() 16 | @Type(() => StepDefinitions) 17 | steps: StepDefinitions; 18 | 19 | @ApiProperty({ 20 | description: 'The initial step of the workflow.' 21 | }) 22 | @IsString() 23 | initialStep: string; 24 | 25 | @ApiProperty({ 26 | description: 'The ID that will be used for the created workflow. Passing an ID is OPTIONAL.' 27 | }) 28 | @IsString() 29 | @IsOptional() 30 | id: string; 31 | } 32 | 33 | export class CreateWorkflowRequestDto { 34 | @ApiProperty({ 35 | description: 'Configuration for a workflow', 36 | type: WorkflowConfigDto 37 | }) 38 | @ValidateNested() 39 | @Type(() => WorkflowConfigDto) 40 | config: WorkflowConfigDto; 41 | } 42 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/create-workflow-success.dto.ts: -------------------------------------------------------------------------------- 1 | import { CreateWorkflowRequestDto } from './create-workflow-request.dto'; 2 | 3 | export class CreateWorkflowSuccessDto extends CreateWorkflowRequestDto {} 4 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/custom-validators/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | export * from './issuer.validator'; 7 | export * from './presentation-definition-credential-query.validator'; 8 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/custom-validators/issuer.validator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { 7 | isObject, 8 | isString, 9 | registerDecorator, 10 | ValidationArguments, 11 | ValidationOptions, 12 | ValidatorConstraint, 13 | ValidatorConstraintInterface, 14 | validate 15 | } from 'class-validator'; 16 | import { IssuerDto } from '../../../credentials/dtos/issuer.dto'; 17 | 18 | @ValidatorConstraint({ async: true }) 19 | export class IsIssuerValidatorConstraint implements ValidatorConstraintInterface { 20 | async validate(value: unknown): Promise { 21 | if (isString(value)) { 22 | return true; 23 | } 24 | 25 | if (isObject(value)) { 26 | const instance = new IssuerDto(value); 27 | const validationErrors = await validate(instance); 28 | // TODO: check how to pass detailed message to the http response 29 | return validationErrors.length === 0; 30 | } 31 | 32 | return false; 33 | } 34 | 35 | defaultMessage(validationArguments?: ValidationArguments): string { 36 | return `${validationArguments.property} is not valid issuer`; 37 | } 38 | } 39 | 40 | export function IsIssuer(options?: ValidationOptions) { 41 | return function (object: unknown, propertyName: string) { 42 | registerDecorator({ 43 | target: object.constructor, 44 | propertyName, 45 | options, 46 | validator: IsIssuerValidatorConstraint 47 | }); 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/custom-validators/presentation-definition-credential-query.validator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IPresentationDefinition, PEX, Status } from '@sphereon/pex'; 7 | import { 8 | registerDecorator, 9 | ValidationArguments, 10 | ValidationOptions, 11 | ValidatorConstraint, 12 | ValidatorConstraintInterface 13 | } from 'class-validator'; 14 | 15 | /** 16 | * https://github.com/typestack/class-validator#custom-validation-decorators 17 | */ 18 | 19 | @ValidatorConstraint({ async: false }) 20 | export class IsPresentationDefinitionCredentialQueryConstraint implements ValidatorConstraintInterface { 21 | validate(value: IPresentationDefinition): boolean { 22 | const pex = new PEX(); 23 | try { 24 | const validated = pex.validateDefinition(value); 25 | const resultArray = Array.isArray(validated) ? validated : [validated]; 26 | const statuses = resultArray.map((checked) => checked.status); 27 | if (statuses.includes(Status.ERROR) || statuses.includes(Status.WARN)) { 28 | return false; 29 | } 30 | return true; 31 | } catch { 32 | return false; 33 | } 34 | } 35 | 36 | defaultMessage(validationArguments?: ValidationArguments): string { 37 | return `${validationArguments.property} is not valid PresentationDefinitionCredentialQuery`; 38 | } 39 | } 40 | 41 | export function IsPresentationDefinitionCredentialQuery(validationOptions?: ValidationOptions) { 42 | return function (object: unknown, propertyName: string) { 43 | registerDecorator({ 44 | target: object.constructor, 45 | propertyName: propertyName, 46 | options: validationOptions, 47 | validator: IsPresentationDefinitionCredentialQueryConstraint 48 | }); 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/exchange-interact-service-definition.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsEnum } from 'class-validator'; 7 | import { VpRequestInteractServiceType } from '../types/vp-request-interact-service-type'; 8 | import { ApiProperty } from '@nestjs/swagger'; 9 | 10 | /** 11 | * A definition of an interact service to be used in a workflow 12 | */ 13 | export class ExchangeInteractServiceDefinitionDto { 14 | @IsEnum(VpRequestInteractServiceType) 15 | @ApiProperty({ 16 | description: 17 | 'The "type" of the interact service.\n' + 18 | 'See Verifiable Presentation Request [Interaction Types](https://w3c-ccg.github.io/vp-request-spec/#interaction-types) for background.', 19 | enum: VpRequestInteractServiceType, 20 | enumName: 'VpRequestInteractServiceType' 21 | }) 22 | type: VpRequestInteractServiceType; 23 | } 24 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/exchange-response.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsOptional, IsString, ValidateNested } from 'class-validator'; 7 | import { VerifiablePresentationDto } from '../../credentials/dtos/verifiable-presentation.dto'; 8 | import { VpRequestDto } from './vp-request.dto'; 9 | import { Type } from 'class-transformer'; 10 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 11 | 12 | /** 13 | * Describes the possible contents of response to a start/continue exchange request 14 | */ 15 | export class ExchangeResponseDto { 16 | @ValidateNested() 17 | @Type(() => VpRequestDto) 18 | @IsOptional() 19 | @ApiPropertyOptional({ 20 | description: 21 | 'Verifiable Presentation Request.\n' + 22 | 'Should conform to VP-Request specification.\n' + 23 | 'Will be returned if a VP is required to obtain more information from requester\n' + 24 | 'May not be returned if no further information is required (for example, at the end of the workflow)' 25 | }) 26 | verifiablePresentationRequest?: VpRequestDto; 27 | 28 | @ValidateNested() 29 | @Type(() => VerifiablePresentationDto) 30 | @IsOptional() 31 | @ApiPropertyOptional({ description: 'If it is an issuance response, then a vp may be provided' }) 32 | verifiablePresentation?: VerifiablePresentationDto; 33 | 34 | @IsString() 35 | @IsOptional() 36 | @ApiProperty({ 37 | description: 'The URL the exchange wishes to redirect the client to.' 38 | }) 39 | redirectUrl?: string; 40 | } 41 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/exchange-state.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString } from 'class-validator'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | import { ExchangeState } from '../types/exchange-status'; 4 | 5 | export class ExchangeStateDto { 6 | @IsString() 7 | @ApiProperty({ 8 | description: 'Exchange Id' 9 | }) 10 | exchangeId: string; 11 | 12 | @IsString() 13 | @ApiProperty({ 14 | description: 'Current step in the exchange' 15 | }) 16 | step: string; 17 | 18 | @IsString() 19 | @ApiProperty({ 20 | description: 'Exchange status' 21 | }) 22 | state: ExchangeState; 23 | } 24 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/exchange-step-state.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsObject, IsOptional, IsString } from 'class-validator'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | import { IssuanceExchangeStep } from '../types/issuance-exchange-step'; 4 | import { QueryExchangeStep } from '../types/query-exchange-step'; 5 | import { ExchangeResponseDto } from './exchange-response.dto'; 6 | 7 | export class ExchangeStepStateDto { 8 | @IsString() 9 | @ApiProperty({ 10 | description: 'Exchange Id' 11 | }) 12 | exchangeId: string; 13 | 14 | @IsString() 15 | @ApiProperty({ 16 | description: 'Step Id' 17 | }) 18 | stepId: string; 19 | 20 | @IsObject() 21 | @ApiProperty({ 22 | description: 'Exchange step information' 23 | }) 24 | step: IssuanceExchangeStep | QueryExchangeStep; 25 | 26 | @IsObject() 27 | @IsOptional() 28 | @ApiProperty({ 29 | description: 'Exchange step information' 30 | }) 31 | stepResponse?: ExchangeResponseDto; 32 | } 33 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/participate-in-exchange.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsBoolean, IsOptional, ValidateNested } from 'class-validator'; 7 | import { VerifiablePresentationDto } from '../../credentials/dtos/verifiable-presentation.dto'; 8 | import { VpRequestDto } from './vp-request.dto'; 9 | import { Type } from 'class-transformer'; 10 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 11 | 12 | /** 13 | * Describes the possible body of a request to participate an exchange. 14 | */ 15 | export class ParticipateInExchangeDto { 16 | @ValidateNested() 17 | @Type(() => VpRequestDto) 18 | @IsOptional() 19 | @ApiPropertyOptional({ 20 | description: 'Verifiable Presentation Request.\n' + 'Should conform to VP Request specification.' 21 | }) 22 | verifiablePresentationRequest?: VpRequestDto; 23 | 24 | @ValidateNested() 25 | @Type(() => VerifiablePresentationDto) 26 | @IsOptional() 27 | @ApiPropertyOptional({ description: 'A JSON-LD Verifiable Presentation with a proof.' }) 28 | verifiablePresentation?: VerifiablePresentationDto; 29 | } 30 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/presentation-definition.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ApiProperty } from '@nestjs/swagger'; 7 | import { IsArray, IsString } from 'class-validator'; 8 | 9 | export class PresentationDefinitionDto { 10 | @IsString() 11 | @ApiProperty() 12 | id: string; 13 | 14 | @IsArray() 15 | @ApiProperty({ type: 'object', isArray: true }) 16 | // TODO: consider defining DTO and validations 17 | input_descriptors: Record[]; 18 | } 19 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/presentation-submission-full.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerificationResultDto } from '../../credentials/dtos/verification-result.dto'; 7 | import { IsNotEmpty, ValidateNested } from 'class-validator'; 8 | import { VerifiablePresentationDto } from '../../credentials/dtos/verifiable-presentation.dto'; 9 | import { Type } from 'class-transformer'; 10 | 11 | /** 12 | * Presentation Submission Full Dto. 13 | * The complete presentation submission, including the submitted Verifiable Presentation 14 | */ 15 | export class PresentationSubmissionFullDto { 16 | /** 17 | * The result of the verification of the submitted VP 18 | */ 19 | @IsNotEmpty() 20 | @ValidateNested() 21 | @Type(() => VerificationResultDto) 22 | verificationResult: VerificationResultDto; 23 | 24 | /** 25 | * The Verifiable Presentation submitted in response to the step's VP Request 26 | */ 27 | @IsNotEmpty() 28 | @ValidateNested() 29 | @Type(() => VerifiablePresentationDto) 30 | verifiablePresentation: VerifiablePresentationDto; 31 | } 32 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/submission-review.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Type } from 'class-transformer'; 7 | import { IsEnum, IsOptional, ValidateNested } from 'class-validator'; 8 | import { VerifiablePresentationDto } from '../../credentials/dtos/verifiable-presentation.dto'; 9 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; 10 | 11 | export enum ReviewResult { 12 | approved = 'approved', 13 | rejected = 'rejected' 14 | } 15 | 16 | export class SubmissionReviewDto { 17 | @IsEnum(ReviewResult) 18 | @ApiProperty({ 19 | description: 'The judgement made by the reviewer', 20 | enum: ReviewResult, 21 | enumName: 'ReviewResult' 22 | }) 23 | result: ReviewResult; 24 | 25 | @ValidateNested() 26 | @IsOptional() 27 | @Type(() => VerifiablePresentationDto) 28 | @ApiPropertyOptional({ 29 | description: 'A reviewer may want to include credentials (wrapped in a VP) to the holder' 30 | }) 31 | vp?: VerifiablePresentationDto; 32 | } 33 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/vp-request-definition.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { Type } from 'class-transformer'; 3 | import { ValidateNested, IsArray } from 'class-validator'; 4 | import { ExchangeInteractServiceDefinitionDto } from './exchange-interact-service-definition.dto'; 5 | import { VpRequestQueryDto } from './vp-request-query.dto'; 6 | 7 | export class VpRequestDefinitionDto { 8 | @ValidateNested() 9 | @IsArray() 10 | @Type(() => ExchangeInteractServiceDefinitionDto) 11 | @ApiProperty({ 12 | description: 13 | 'The Interact Service Definitions are related to the Interaction Types of the Verifiable Presentation Request (VPR) specification.\n' + 14 | 'However, as it is a configuration object, it not identical to a VPR interact services.\n' + 15 | 'It can be see as the input data that the application uses to generate VPR interact services during the exchanges.', 16 | type: ExchangeInteractServiceDefinitionDto, 17 | isArray: true 18 | }) 19 | interactServices: ExchangeInteractServiceDefinitionDto[]; 20 | 21 | @ValidateNested({ each: true }) 22 | @IsArray() 23 | @Type(() => VpRequestQueryDto) 24 | @ApiProperty({ 25 | description: 'Defines requests for data in the Verifiable Presentation', 26 | type: VpRequestQueryDto, 27 | isArray: true 28 | }) 29 | query: VpRequestQueryDto[]; 30 | } 31 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/vp-request-did-auth-query.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | export class VpRequestDidAuthQueryDto {} 7 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/vp-request-presentation-defintion-query.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsPresentationDefinitionCredentialQuery } from './custom-validators'; 7 | import { ApiProperty } from '@nestjs/swagger'; 8 | import { IsNotEmptyObject, ValidateNested } from 'class-validator'; 9 | import { PresentationDefinitionDto } from './presentation-definition.dto'; 10 | import { Type } from 'class-transformer'; 11 | 12 | /** 13 | * https://github.com/w3c-ccg/vp-request-spec/issues/7#issuecomment-1067036904 14 | */ 15 | export class VpRequestPresentationDefinitionQueryDto { 16 | @ValidateNested() 17 | @IsPresentationDefinitionCredentialQuery() 18 | @IsNotEmptyObject() 19 | @Type(() => PresentationDefinitionDto) 20 | @ApiProperty({ 21 | description: 22 | 'An object conforming to the Presentation Definition specification\n' + 23 | 'https://github.com/w3c-ccg/vp-request-spec/issues/7#issuecomment-1067036904', 24 | type: PresentationDefinitionDto 25 | }) 26 | presentationDefinition: PresentationDefinitionDto; 27 | } 28 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/vp-request.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsArray, ValidateNested } from 'class-validator'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | import { Type } from 'class-transformer'; 4 | import { VpRequestQueryDto } from './vp-request-query.dto'; 5 | import { VpRequestInteractDto } from '../../exchanges/dtos/vp-request-interact.dto'; 6 | 7 | export class VpRequestDto { 8 | @ApiProperty({ 9 | description: 'A set of one or more queries sent by the requester.', 10 | type: [VpRequestQueryDto] 11 | }) 12 | @IsArray() 13 | @ValidateNested({ each: true }) 14 | @Type(() => VpRequestQueryDto) 15 | query: VpRequestQueryDto[]; 16 | 17 | @ApiProperty({ 18 | description: 19 | 'A challenge, intended to prevent replay attacks, provided by the requester that is typically expected to be included in the Verifiable Presentation response.' 20 | }) 21 | @IsString() 22 | challenge: string; 23 | 24 | @ApiProperty({ 25 | description: 'A list of interaction mechanisms that are supported by the server.', 26 | type: [VpRequestInteractDto] 27 | }) 28 | @ValidateNested() 29 | @Type(() => VpRequestInteractDto) 30 | interact: VpRequestInteractDto; 31 | 32 | @ApiProperty({ 33 | description: 'A domain string to prevent replay attacks.' 34 | }) 35 | @IsString() 36 | domain: string; 37 | } 38 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/workflow-step-definition.dto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IsArray, IsObject, IsString, ValidateNested } from 'class-validator'; 7 | import { CallbackConfigurationDto } from './callback-configuration.dto'; 8 | import { Type } from 'class-transformer'; 9 | import { ApiProperty } from '@nestjs/swagger'; 10 | import { VpRequestDefinitionDto } from './vp-request-definition.dto'; 11 | 12 | export class WorkflowStepDefinitionDto { 13 | @ValidateNested({ each: true }) 14 | @IsObject() 15 | @Type(() => VpRequestDefinitionDto) 16 | @ApiProperty({ 17 | description: 'A Verifiable Presentation Request object', 18 | type: VpRequestDefinitionDto 19 | }) 20 | verifiablePresentationRequest: VpRequestDefinitionDto; 21 | 22 | @IsString() 23 | @ApiProperty({ 24 | description: 'The next step after the current step' 25 | }) 26 | nextStep: string; 27 | 28 | @ValidateNested({ each: true }) 29 | @IsArray() 30 | @Type(() => CallbackConfigurationDto) 31 | @ApiProperty({ 32 | description: 33 | 'An array of "callbacks" that will be used by VC API to send notifications on the status/result of the exchange.', 34 | type: CallbackConfigurationDto, 35 | isArray: true 36 | }) 37 | callback: CallbackConfigurationDto[]; 38 | } 39 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/dtos/workflow-step.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsArray, ValidateNested, IsOptional } from 'class-validator'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | import { Type } from 'class-transformer'; 4 | import { CallbackConfigurationDto } from './callback-configuration.dto'; 5 | import { VpRequestDto } from './vp-request.dto'; 6 | 7 | export class WorkflowStepDto { 8 | @ValidateNested({ each: true }) 9 | @IsArray() 10 | @Type(() => CallbackConfigurationDto) 11 | @IsOptional() 12 | @ApiProperty({ 13 | description: 14 | 'An array of "callbacks" that will be used by VC API to send notifications on the status/result of the exchange.', 15 | type: CallbackConfigurationDto, 16 | isArray: true 17 | }) 18 | callback: CallbackConfigurationDto[]; 19 | 20 | @ApiProperty({ 21 | description: 'A verifiable presentation request object.', 22 | type: VpRequestDto 23 | }) 24 | @ValidateNested() 25 | @Type(() => VpRequestDto) 26 | verifiablePresentationRequest: VpRequestDto; 27 | } 28 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/entities/workflow.entity.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Column, Entity } from 'typeorm'; 7 | import { v4 as uuidv4 } from 'uuid'; 8 | import { StepDefinitions, WorkflowConfigDto } from '../dtos/create-workflow-request.dto'; 9 | import { WorkflowStepDefinitionDto } from '../dtos/workflow-step-definition.dto'; 10 | 11 | /** 12 | * A TypeOrm entity representing an exchange 13 | * https://w3c-ccg.github.io/vc-api/#exchange-examples 14 | * 15 | * Some discussion regarding the rational behind the names: 16 | * https://github.com/w3c-ccg/vc-api/pull/262#discussion_r805895143 17 | * 18 | * An exchange does not keep reference to its transactions 19 | * as the number of transactions grow quite high for a reusable exchange (e.g. "issue-degree" could issues thousands of degrees) 20 | */ 21 | @Entity() 22 | export class WorkflowEntity { 23 | constructor(workflowConfig: WorkflowConfigDto) { 24 | this.workflowId = workflowConfig?.id ?? uuidv4(); 25 | this.workflowSteps = workflowConfig?.steps; 26 | this.initialStep = workflowConfig?.initialStep; 27 | } 28 | 29 | @Column('text', { primary: true }) 30 | workflowId: string; 31 | 32 | @Column('simple-json') 33 | workflowSteps: StepDefinitions; 34 | 35 | @Column('text') 36 | initialStep: string; 37 | 38 | /** 39 | * Get the first step in a workflow. 40 | * This can then be used to create a new exchange based on this workflow. 41 | * 42 | * @returns the first step in the workflow 43 | */ 44 | public getInitialStep(): WorkflowStepDefinitionDto { 45 | return this.workflowSteps[this.initialStep]; 46 | } 47 | 48 | /** 49 | * Get the next step from the current step definition 50 | * 51 | * @param currentStep 52 | * @returns the next step in the workflow 53 | */ 54 | public getNextStep(currentStep: string): [WorkflowStepDefinitionDto, string] { 55 | const nextStep = this.workflowSteps[currentStep].nextStep; 56 | return [nextStep ? this.workflowSteps[nextStep] : null, nextStep]; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/callback-configuration.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * A callback to execute after an exchange 8 | */ 9 | export interface CallbackConfiguration { 10 | url: string; 11 | } 12 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/credential.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * A JSON-LD Verifiable Credential without a proof. 8 | * https://w3c-ccg.github.io/vc-api/issuer.html#operation/issueCredential 9 | */ 10 | export interface Credential { 11 | // Needed to match @sphereon/pex type 12 | // https://github.com/Sphereon-Opensource/pex/blob/a1f56d6baabf1b0e1e28a08d04ffc97d76863207/lib/types/SSI.types.ts#L87 13 | [x: string]: unknown; 14 | 15 | '@context': Array>; 16 | 17 | /** 18 | * The ID of the credential. 19 | */ 20 | id?: string; 21 | 22 | /** 23 | * The JSON-LD type of the credential. 24 | */ 25 | type: string[]; 26 | 27 | /** 28 | * A JSON-LD Verifiable Credential Issuer. 29 | */ 30 | issuer: string | { id: string }; 31 | 32 | /** 33 | * The issuanceDate 34 | */ 35 | issuanceDate: string; 36 | 37 | /** 38 | * The expirationDate 39 | */ 40 | expirationDate?: string; 41 | 42 | /** 43 | * The subject 44 | */ 45 | credentialSubject: Record; 46 | } 47 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/exchange-status.ts: -------------------------------------------------------------------------------- 1 | export enum ExchangeState { 2 | pending = 'pending', 3 | active = 'active', 4 | completed = 'completed', 5 | invalid = 'invalid' 6 | } 7 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/exchange-step.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ExchangeResponseDto } from '../dtos/exchange-response.dto'; 7 | import { CallbackConfiguration } from './callback-configuration'; 8 | import { VerifiablePresentation } from '../types/verifiable-presentation'; 9 | import { SubmissionVerifier } from './submission-verifier'; 10 | import { VerificationResultDto } from '../../credentials/dtos/verification-result.dto'; 11 | 12 | export const EXCHANGE_STEP_STATES = { 13 | IN_PROGRESS: 'in-progress', 14 | COMPLETE: 'complete' 15 | } as const; 16 | 17 | export type ExchangeStepState = (typeof EXCHANGE_STEP_STATES)[keyof typeof EXCHANGE_STEP_STATES]; 18 | 19 | export abstract class ExchangeStep { 20 | constructor(stepId: string, callback: CallbackConfiguration[], type: string) { 21 | this.stepId = stepId; 22 | this.callback = callback; 23 | this.type = type; 24 | this._state = EXCHANGE_STEP_STATES.IN_PROGRESS; 25 | } 26 | 27 | stepId: string; 28 | private _state: ExchangeStepState; 29 | callback: CallbackConfiguration[]; 30 | type: string; 31 | 32 | public abstract processPresentation( 33 | presentation: VerifiablePresentation, 34 | verifier: SubmissionVerifier 35 | ): Promise<{ errors: string[]; verificationResult: VerificationResultDto }>; 36 | 37 | public abstract getStepResponse(): ExchangeResponseDto; 38 | 39 | public get isComplete(): boolean { 40 | return this._state == EXCHANGE_STEP_STATES.COMPLETE; 41 | } 42 | 43 | protected markComplete(): void { 44 | this._state = EXCHANGE_STEP_STATES.COMPLETE; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/issuance-exchange-step.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerifiablePresentation } from '../types/verifiable-presentation'; 7 | import { CallbackConfiguration } from './callback-configuration'; 8 | import { ExchangeStep } from './exchange-step'; 9 | import { ExchangeResponseDto } from '../dtos/exchange-response.dto'; 10 | import { VerificationResultDto } from '../../credentials/dtos/verification-result.dto'; 11 | 12 | export class IssuanceExchangeStep extends ExchangeStep { 13 | constructor(stepId: string, callback: CallbackConfiguration[], holderRedirectUrl: string) { 14 | super(stepId, callback, 'IssuanceExchangeStep'); 15 | this.holderRedirectUrl = holderRedirectUrl; 16 | } 17 | 18 | holderRedirectUrl: string; 19 | 20 | issuedVP?: VerifiablePresentation; 21 | 22 | public addVP(issuanceVp: VerifiablePresentation): void { 23 | this.issuedVP = issuanceVp; 24 | this.markComplete(); 25 | } 26 | 27 | /** 28 | * An issuance exchange can process a presentation, but it won't take any action. 29 | * It is essentially a "no op". 30 | */ 31 | public processPresentation(): Promise<{ 32 | errors: string[]; 33 | verificationResult: VerificationResultDto; 34 | }> { 35 | return Promise.resolve({ 36 | errors: [], 37 | verificationResult: null 38 | }); 39 | } 40 | 41 | public getStepResponse(): ExchangeResponseDto { 42 | if (this.issuedVP) { 43 | return { 44 | verifiablePresentation: this.issuedVP 45 | }; 46 | } 47 | // As the issuer hasn't provided VP yet, holder needs to poll until response updates 48 | else { 49 | return { 50 | redirectUrl: this.holderRedirectUrl 51 | }; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/presentation-submission.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerificationResult } from '../../credentials/types/verification-result'; 7 | // TODO: move to a common place (can probably be in the credentials module) 8 | import { VerifiablePresentation } from '../../exchanges/types/verifiable-presentation'; 9 | 10 | export class PresentationSubmission { 11 | constructor(vp: VerifiablePresentation, verificationResult: VerificationResult) { 12 | this.verifiablePresentation = vp; 13 | this.verificationResult = verificationResult; 14 | } 15 | /** 16 | * The result of the verification of the submitted VP 17 | */ 18 | verificationResult: VerificationResult; 19 | 20 | verifiablePresentation: VerifiablePresentation; 21 | } 22 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/presentation.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerifiableCredential } from './verifiable-credential'; 7 | 8 | /** 9 | * A JSON-LD Verifiable Presentation without a proof. 10 | * https://w3c-ccg.github.io/vc-api/holder.html#operation/provePresentation 11 | */ 12 | export interface Presentation { 13 | /** 14 | * The JSON-LD context of the presentation. 15 | */ 16 | '@context': Array>; 17 | 18 | /** 19 | * The ID of the presentation. 20 | * The id property is optional and MAY be used to provide a unique identifier for the presentation. 21 | * https://www.w3.org/TR/vc-data-model/#presentations-0 22 | */ 23 | id?: string; 24 | 25 | /** 26 | * The JSON-LD type of the presentation. 27 | */ 28 | type: string[]; 29 | 30 | /** 31 | * The holder - will be ignored if no proof is present since there is no proof of authority over the credentials 32 | */ 33 | holder?: string; 34 | 35 | /** 36 | * The Verifiable Credentials 37 | */ 38 | verifiableCredential?: VerifiableCredential[]; 39 | } 40 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/query-exchange-step.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { ExchangeResponseDto } from '../dtos/exchange-response.dto'; 7 | import { VpRequestDto } from '../dtos/vp-request.dto'; 8 | import { CallbackConfiguration } from './callback-configuration'; 9 | import { ExchangeStep } from './exchange-step'; 10 | import { SubmissionVerifier } from './submission-verifier'; 11 | import { VerifiablePresentation } from '../types/verifiable-presentation'; 12 | import { PresentationSubmission } from './presentation-submission'; 13 | import { Column } from 'typeorm'; 14 | import { VerificationResultDto } from '../../credentials/dtos/verification-result.dto'; 15 | 16 | export class QueryExchangeStep extends ExchangeStep { 17 | constructor(stepId: string, vpRequest: VpRequestDto, callback: CallbackConfiguration[]) { 18 | super(stepId, callback, 'QueryExchangeStep'); 19 | this.vpRequest = vpRequest; 20 | } 21 | 22 | @Column('simple-json') 23 | vpRequest: VpRequestDto; 24 | 25 | @Column('simple-json') 26 | presentationSubmission?: PresentationSubmission; 27 | 28 | /** 29 | * Process a presentation submission. 30 | * @param presentation 31 | * @param verifier 32 | */ 33 | public async processPresentation( 34 | presentation: VerifiablePresentation, 35 | verifier: SubmissionVerifier 36 | ): Promise<{ errors: string[]; verificationResult: VerificationResultDto }> { 37 | const verificationResult = await verifier.verifyVpRequestSubmission(presentation, this.vpRequest); 38 | 39 | const errors = verificationResult.errors; 40 | this.presentationSubmission = new PresentationSubmission(presentation, verificationResult); 41 | // If no errors, then assume that verification was successful and so query is complete 42 | if (errors.length == 0) { 43 | this.markComplete(); 44 | } 45 | return { errors, verificationResult }; 46 | } 47 | 48 | public getStepResponse(): ExchangeResponseDto { 49 | return { 50 | verifiablePresentationRequest: this.vpRequest 51 | }; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/submission-verifier.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VerificationResultDto } from '../../credentials/dtos/verification-result.dto'; 7 | import { VpRequestDto } from '../dtos/vp-request.dto'; 8 | // TODO: move to a common place (can probably be in the credentials module) 9 | import { VerifiablePresentation } from '../../exchanges/types/verifiable-presentation'; 10 | 11 | /** 12 | * Intended to represent a verifier of a VP Request Submission. 13 | * TODO: Maybe shouldn't only be for VPR verification but allow for more generic types. 14 | */ 15 | export interface SubmissionVerifier { 16 | verifyVpRequestSubmission: ( 17 | vp: VerifiablePresentation, 18 | vpRequest: VpRequestDto 19 | ) => Promise; 20 | } 21 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/verifiable-credential.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IProof } from '@sphereon/pex'; 7 | import { Credential } from './credential'; 8 | 9 | /** 10 | * A JSON-LD Verifiable Credential with a proof. 11 | * https://w3c-ccg.github.io/vc-api/issuer.html#operation/issueCredential 12 | */ 13 | export interface VerifiableCredential extends Credential { 14 | /** 15 | * A JSON-LD Linked Data proof. 16 | */ 17 | proof: Record | IProof; 18 | } 19 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/verifiable-presentation.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Presentation } from './presentation'; 7 | 8 | /** 9 | * A JSON-LD Verifiable Presentation with a proof. 10 | * https://w3c-ccg.github.io/vc-api/verifier.html#operation/verifyPresentation 11 | */ 12 | export interface VerifiablePresentation extends Presentation { 13 | /** 14 | * A JSON-LD Linked Data proof. 15 | */ 16 | proof: Record; 17 | } 18 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/vp-request-interact-service-type.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * The interact service types that are both 8 | * - supported by the wallet app 9 | * - listed in the VP Request spec https://w3c-ccg.github.io/vp-request-spec/#interaction-types 10 | */ 11 | export enum VpRequestInteractServiceType { 12 | /** 13 | * https://w3c-ccg.github.io/vp-request-spec/#unmediated-presentation 14 | */ 15 | unmediatedPresentation = 'UnmediatedHttpPresentationService2021', 16 | 17 | /** 18 | * See https://w3c-ccg.github.io/vp-request-spec/#mediated-presentation for background. 19 | * Note that the specification (as of v0.1, 25-04-2022), refers to "MediatedBrowserPresentationService2021". 20 | * This [GitHub issue](https://github.com/w3c-ccg/vp-request-spec/issues/17) is open to discuss the usage of Mediated Presentations Services 21 | */ 22 | mediatedPresentation = 'MediatedHttpPresentationService2021' 23 | } 24 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/vp-request-query-type.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * Query types as listed in the VP Request spec. 8 | * https://w3c-ccg.github.io/vp-request-spec/#query-types 9 | * 10 | */ 11 | export enum VpRequestQueryType { 12 | /** 13 | * https://w3c-ccg.github.io/vp-request-spec/#did-authentication-request 14 | */ 15 | didAuth = 'DIDAuth', 16 | 17 | /** 18 | * A presentation definition https://identity.foundation/presentation-exchange/#presentation-definition 19 | * This type is proposed here: https://github.com/w3c-ccg/vp-request-spec/issues/7 20 | */ 21 | presentationDefinition = 'PresentationDefinition' 22 | } 23 | -------------------------------------------------------------------------------- /apps/vc-api/src/vc-api/workflows/types/vp-request-query.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { VpRequestQueryType } from './vp-request-query-type'; 7 | 8 | /** 9 | * From https://w3c-ccg.github.io/vp-request-spec/#format : 10 | * "To make a request for one or more objects wrapped in a Verifiable Presentation, 11 | * a client constructs a JSON request describing one or more queries that it wishes to perform from the receiver." 12 | * "The query type serves as the main extension point mechanism for requests for data in the presentation. 13 | * This document defines several common query types." 14 | */ 15 | export interface VpRequestQuery { 16 | type: VpRequestQueryType; 17 | credentialQuery: any; 18 | } 19 | -------------------------------------------------------------------------------- /apps/vc-api/static-assets/.well-known/assetlinks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "relation": ["delegate_permission/common.handle_all_urls"], 4 | "target": { 5 | "namespace": "android_app", 6 | "package_name": "com.example.mobilewallet", 7 | "sha256_cert_fingerprints": [ 8 | "DE:FB:6D:06:CB:89:AE:44:01:B9:5A:76:CF:53:21:F9:C8:BF:B3:65:54:7D:F6:3B:36:81:A2:06:6E:56:D2:82" 9 | ] 10 | } 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /apps/vc-api/test/did/did.e2e-suite.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { walletClient } from '../app.e2e-spec'; 7 | 8 | export const didSuite = () => { 9 | // it('should create and retrieve a new did:ethr DID', async () => { 10 | // await walletClient.createDID('ethr'); 11 | // }); 12 | 13 | it('should create and retrieve a new did:key DID', async () => { 14 | await walletClient.createDID('key'); 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /apps/vc-api/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/vc-api/test/key/key.service.spec.data.ts: -------------------------------------------------------------------------------- 1 | import { KeyEntryObject } from '@hyperledger/aries-askar-shared'; 2 | import { Key } from '@credo-ts/core'; 3 | 4 | export const createdKey: Key = { 5 | keyType: 'ed25519', 6 | publicKeyBase58: '6qZag5woSLKdVtKW37hBPNZGrJFzR4HiXvsADAZY1seC' 7 | } as any; 8 | 9 | export const keyEntryObject: KeyEntryObject = { 10 | key: { 11 | get jwkPublic() { 12 | return { 13 | kty: 'OKP', 14 | crv: 'Ed25519', 15 | x: 'VrsnBw1-JP3R4xuaQqDQI9pXM2YP1Per79Unm2UkCaU', 16 | kid: '6qZag5woSLKdVtKW37hBPNZGrJFzR4HiXvsADAZY1seC' 17 | }; 18 | } 19 | } as any 20 | } as any; 21 | -------------------------------------------------------------------------------- /apps/vc-api/test/vc-api/credential.service.spec.key.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | export const key = { 7 | kty: 'OKP', 8 | crv: 'Ed25519', 9 | x: 'gZbb93kdEoQ9Be78z7NG064wBq8Vv_0zR-qglxkiJ-g', 10 | d: 'XXugDYUEtINLUefOLeztqOOtPukVIvNPreMTzl6wKgA', 11 | kid: 'zWME913jdYrILuYD-ot-jDbmzqz34HqlCUZ6CMdJnyo' 12 | }; 13 | 14 | export const did = 'did:key:z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF'; 15 | export const didDoc = { 16 | id: 'did:key:z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF', 17 | verificationMethod: [ 18 | { 19 | id: 'did:key:z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF#z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF', 20 | type: 'Ed25519VerificationKey2018', 21 | controller: 'did:key:z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF', 22 | publicKeyJwk: { 23 | kty: 'OKP', 24 | crv: 'Ed25519', 25 | x: 'gZbb93kdEoQ9Be78z7NG064wBq8Vv_0zR-qglxkiJ-g', 26 | d: 'XXugDYUEtINLUefOLeztqOOtPukVIvNPreMTzl6wKgA', 27 | kid: 'zWME913jdYrILuYD-ot-jDbmzqz34HqlCUZ6CMdJnyo' 28 | } 29 | } 30 | ] 31 | }; 32 | 33 | export const challenge = '2679f7f3-d9ff-4a7e-945c-0f30fb0765bd'; 34 | -------------------------------------------------------------------------------- /apps/vc-api/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/vc-api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2017", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "allowJs": true, 16 | "resolveJsonModule": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /common/autoinstallers/rush-prettier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rush-prettier", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "prettier": "^2.8.8", 7 | "pretty-quick": "^3.1.3" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /common/config/rush/.npmrc: -------------------------------------------------------------------------------- 1 | # Rush uses this file to configure the NPM package registry during installation. It is applicable 2 | # to PNPM, NPM, and Yarn package managers. It is used by operations such as "rush install", 3 | # "rush update", and the "install-run.js" scripts. 4 | # 5 | # NOTE: The "rush publish" command uses .npmrc-publish instead. 6 | # 7 | # Before invoking the package manager, Rush will copy this file to the folder where installation 8 | # is performed. The copied file will omit any config lines that reference environment variables 9 | # that are undefined in that session; this avoids problems that would otherwise result due to 10 | # a missing variable being replaced by an empty string. 11 | # 12 | # * * * SECURITY WARNING * * * 13 | # 14 | # It is NOT recommended to store authentication tokens in a text file on a lab machine, because 15 | # other unrelated processes may be able to read the file. Also, the file may persist indefinitely, 16 | # for example if the machine loses power. A safer practice is to pass the token via an 17 | # environment variable, which can be referenced from .npmrc using ${} expansion. For example: 18 | # 19 | # //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} 20 | # 21 | registry=https://registry.npmjs.org/ 22 | always-auth=false 23 | -------------------------------------------------------------------------------- /common/config/rush/.npmrc-publish: -------------------------------------------------------------------------------- 1 | # This config file is very similar to common/config/rush/.npmrc, except that .npmrc-publish 2 | # is used by the "rush publish" command, as publishing often involves different credentials 3 | # and registries than other operations. 4 | # 5 | # Before invoking the package manager, Rush will copy this file to "common/temp/publish-home/.npmrc" 6 | # and then temporarily map that folder as the "home directory" for the current user account. 7 | # This enables the same settings to apply for each project folder that gets published. The copied file 8 | # will omit any config lines that reference environment variables that are undefined in that session; 9 | # this avoids problems that would otherwise result due to a missing variable being replaced by 10 | # an empty string. 11 | # 12 | # * * * SECURITY WARNING * * * 13 | # 14 | # It is NOT recommended to store authentication tokens in a text file on a lab machine, because 15 | # other unrelated processes may be able to read the file. Also, the file may persist indefinitely, 16 | # for example if the machine loses power. A safer practice is to pass the token via an 17 | # environment variable, which can be referenced from .npmrc using ${} expansion. For example: 18 | # 19 | # //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} 20 | # 21 | -------------------------------------------------------------------------------- /common/config/rush/.pnpmfile.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * When using the PNPM package manager, you can use pnpmfile.js to workaround 5 | * dependencies that have mistakes in their package.json file. (This feature is 6 | * functionally similar to Yarn's "resolutions".) 7 | * 8 | * For details, see the PNPM documentation: 9 | * https://pnpm.js.org/docs/en/hooks.html 10 | * 11 | * IMPORTANT: SINCE THIS FILE CONTAINS EXECUTABLE CODE, MODIFYING IT IS LIKELY TO INVALIDATE 12 | * ANY CACHED DEPENDENCY ANALYSIS. After any modification to pnpmfile.js, it's recommended to run 13 | * "rush update --full" so that PNPM will recalculate all version selections. 14 | */ 15 | module.exports = { 16 | hooks: { 17 | readPackage 18 | } 19 | }; 20 | 21 | /** 22 | * This hook is invoked during installation before a package's dependencies 23 | * are selected. 24 | * The `packageJson` parameter is the deserialized package.json 25 | * contents for the package that is about to be installed. 26 | * The `context` parameter provides a log() function. 27 | * The return value is the updated object. 28 | */ 29 | function readPackage(packageJson, context) { 30 | 31 | // // The karma types have a missing dependency on typings from the log4js package. 32 | // if (packageJson.name === '@types/karma') { 33 | // context.log('Fixed up dependencies for @types/karma'); 34 | // packageJson.dependencies['log4js'] = '0.6.38'; 35 | // } 36 | 37 | return packageJson; 38 | } 39 | -------------------------------------------------------------------------------- /common/config/rush/repo-state.json: -------------------------------------------------------------------------------- 1 | // DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. 2 | { 3 | "preferredVersionsHash": "bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f" 4 | } 5 | -------------------------------------------------------------------------------- /common/git-hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This is an example Git hook for use with Rush. To enable this hook, rename this file 4 | # to "commit-msg" and then run "rush install", which will copy it from common/git-hooks 5 | # to the .git/hooks folder. 6 | # 7 | # TO LEARN MORE ABOUT GIT HOOKS 8 | # 9 | # The Git documentation is here: https://git-scm.com/docs/githooks 10 | # Some helpful resources: https://githooks.com 11 | # 12 | # ABOUT THIS EXAMPLE 13 | # 14 | # The commit-msg hook is called by "git commit" with one argument, the name of the file 15 | # that has the commit message. The hook should exit with non-zero status after issuing 16 | # an appropriate message if it wants to stop the commit. The hook is allowed to edit 17 | # the commit message file. 18 | 19 | # This example enforces that commit message should contain a minimum amount of 20 | # description text. 21 | if [ `cat $1 | wc -w` -lt 3 ]; then 22 | echo "" 23 | echo "Invalid commit message: The message must contain at least 3 words." 24 | exit 1 25 | fi 26 | -------------------------------------------------------------------------------- /common/git-hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2021 - 2023 Energy Web Foundation 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | # Called by "git commit" with no arguments. The hook should 18 | # exit with non-zero status after issuing an appropriate message if 19 | # it wants to stop the commit. 20 | 21 | # Invoke the "rush prettier" custom command to reformat files whenever they 22 | # are committed. The command is defined in common/config/rush/command-line.json 23 | # and uses the "rush-prettier" autoinstaller. 24 | node common/scripts/install-run-rush.js update-openapi --only @energyweb/ssi-vc-api || exit $? 25 | node common/scripts/install-run-rush.js prettier || exit $? 26 | -------------------------------------------------------------------------------- /common/pnpm-patches/@credo-ts__core@0.5.10.patch: -------------------------------------------------------------------------------- 1 | diff --git a/build/modules/vc/data-integrity/W3cJsonLdCredentialService.js b/build/modules/vc/data-integrity/W3cJsonLdCredentialService.js 2 | index c833d0108e8852127afac1c87cffca0aba1268dc..e3391b11fd9b7b7fe85b027eb68ea110e4324499 100644 3 | --- a/build/modules/vc/data-integrity/W3cJsonLdCredentialService.js 4 | +++ b/build/modules/vc/data-integrity/W3cJsonLdCredentialService.js 5 | @@ -179,7 +179,7 @@ let W3cJsonLdCredentialService = class W3cJsonLdCredentialService { 6 | domain: options.domain, 7 | documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), 8 | }); 9 | - return utils_1.JsonTransformer.fromJSON(result, W3cJsonLdVerifiablePresentation_1.W3cJsonLdVerifiablePresentation); 10 | + return utils_1.JsonTransformer.fromJSON(result, W3cJsonLdVerifiablePresentation_1.W3cJsonLdVerifiablePresentation, { validate: !(result.verifiableCredential === undefined) }); 11 | } 12 | /** 13 | * Verifies a presentation including the credentials it includes 14 | -------------------------------------------------------------------------------- /common/scripts/install-run-rush-pnpm.js: -------------------------------------------------------------------------------- 1 | // THIS FILE WAS GENERATED BY A TOOL. ANY MANUAL MODIFICATIONS WILL GET OVERWRITTEN WHENEVER RUSH IS UPGRADED. 2 | // 3 | // This script is intended for usage in an automated build environment where the Rush command may not have 4 | // been preinstalled, or may have an unpredictable version. This script will automatically install the version of Rush 5 | // specified in the rush.json configuration file (if not already installed), and then pass a command-line to the 6 | // rush-pnpm command. 7 | // 8 | // An example usage would be: 9 | // 10 | // node common/scripts/install-run-rush-pnpm.js pnpm-command 11 | // 12 | // For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ 13 | 14 | /******/ (() => { // webpackBootstrap 15 | /******/ "use strict"; 16 | var __webpack_exports__ = {}; 17 | /*!*****************************************************!*\ 18 | !*** ./lib-esnext/scripts/install-run-rush-pnpm.js ***! 19 | \*****************************************************/ 20 | 21 | // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 22 | // See the @microsoft/rush package's LICENSE file for license information. 23 | require('./install-run-rush'); 24 | //# sourceMappingURL=install-run-rush-pnpm.js.map 25 | module.exports = __webpack_exports__; 26 | /******/ })() 27 | ; 28 | //# sourceMappingURL=install-run-rush-pnpm.js.map -------------------------------------------------------------------------------- /common/scripts/install-run-rushx.js: -------------------------------------------------------------------------------- 1 | // THIS FILE WAS GENERATED BY A TOOL. ANY MANUAL MODIFICATIONS WILL GET OVERWRITTEN WHENEVER RUSH IS UPGRADED. 2 | // 3 | // This script is intended for usage in an automated build environment where the Rush command may not have 4 | // been preinstalled, or may have an unpredictable version. This script will automatically install the version of Rush 5 | // specified in the rush.json configuration file (if not already installed), and then pass a command-line to the 6 | // rushx command. 7 | // 8 | // An example usage would be: 9 | // 10 | // node common/scripts/install-run-rushx.js custom-command 11 | // 12 | // For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ 13 | 14 | /******/ (() => { // webpackBootstrap 15 | /******/ "use strict"; 16 | var __webpack_exports__ = {}; 17 | /*!*************************************************!*\ 18 | !*** ./lib-esnext/scripts/install-run-rushx.js ***! 19 | \*************************************************/ 20 | 21 | // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 22 | // See the @microsoft/rush package's LICENSE file for license information. 23 | require('./install-run-rush'); 24 | //# sourceMappingURL=install-run-rushx.js.map 25 | module.exports = __webpack_exports__; 26 | /******/ })() 27 | ; 28 | //# sourceMappingURL=install-run-rushx.js.map -------------------------------------------------------------------------------- /docs/image-src.txt: -------------------------------------------------------------------------------- 1 | Source files for the images are available here: 2 | 3 | - Component diagram: https://docs.google.com/drawings/d/1HMEzctdQpapeff2amePwDL5G0ny8F9HkAQqKytSi9Hs/edit?usp=sharing -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwallet-foundation-labs/vc-api/d8cd9adfcc9a30e73682c9728b053f569575ebcc/index.md -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: true, 3 | transform: { 4 | '\\.(ts|tsx)$': 'ts-jest', 5 | '\\.mjs$': 'babel-jest' 6 | }, 7 | transformIgnorePatterns: ['node_modules/.+\\.!mjs$'], 8 | testEnvironment: 'node', 9 | testRegex: '(/__tests__/.*|\\.(test|spec))\\.(ts|tsx)$', 10 | moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], 11 | coveragePathIgnorePatterns: ['/node_modules/', '/test/'], 12 | coverageThreshold: { 13 | global: { 14 | branches: 0, 15 | functions: 0, 16 | lines: 0, 17 | statements: 0 18 | } 19 | }, 20 | collectCoverageFrom: ['src/*.{js,ts}'] 21 | }; 22 | -------------------------------------------------------------------------------- /libraries/did/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /libraries/did/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | module.exports = { 7 | parserOptions: { 8 | tsconfigRootDir: __dirname, 9 | project: 'tsconfig.json' 10 | }, 11 | extends: ['@energyweb'], 12 | env: { 13 | es2021: true, 14 | node: true, 15 | jest: true 16 | }, 17 | ignorePatterns: ['.eslintrc.js'] 18 | }; 19 | -------------------------------------------------------------------------------- /libraries/did/CHANGELOG.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@energyweb/ev-did-hydrator", 3 | "entries": [ 4 | { 5 | "version": "1.6.0", 6 | "tag": "@energyweb/ev-did-hydrator_v1.6.0", 7 | "date": "Thu, 09 Sep 2021 09:10:31 GMT", 8 | "comments": {} 9 | }, 10 | { 11 | "version": "1.5.0", 12 | "tag": "@energyweb/ev-did-hydrator_v1.5.0", 13 | "date": "Mon, 16 Aug 2021 22:23:27 GMT", 14 | "comments": { 15 | "none": [ 16 | { 17 | "comment": "correctly creation claim so that it can be verified. Also added method to create Operator DID Document" 18 | } 19 | ] 20 | } 21 | }, 22 | { 23 | "version": "1.4.0", 24 | "tag": "@energyweb/ev-did-hydrator_v1.4.0", 25 | "date": "Thu, 29 Jul 2021 12:49:07 GMT", 26 | "comments": {} 27 | }, 28 | { 29 | "version": "1.3.0", 30 | "tag": "@energyweb/ev-did-hydrator_v1.3.0", 31 | "date": "Fri, 18 Jun 2021 00:21:50 GMT", 32 | "comments": {} 33 | }, 34 | { 35 | "version": "1.2.0", 36 | "tag": "@energyweb/ev-did-hydrator_v1.2.0", 37 | "date": "Tue, 15 Jun 2021 23:24:58 GMT", 38 | "comments": {} 39 | }, 40 | { 41 | "version": "1.1.0", 42 | "tag": "@energyweb/ev-did-hydrator_v1.1.0", 43 | "date": "Tue, 04 May 2021 15:35:33 GMT", 44 | "comments": { 45 | "none": [ 46 | { 47 | "comment": "add addClaim to DID class. Also can pass in mint amount to createDocument." 48 | }, 49 | { 50 | "comment": "remove unused param in DID.test.ts" 51 | } 52 | ] 53 | } 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /libraries/did/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log - @energyweb/ev-did-hydrator 2 | 3 | This log was last generated on Thu, 09 Sep 2021 09:10:31 GMT and should not be manually modified. 4 | 5 | ## 1.6.0 6 | Thu, 09 Sep 2021 09:10:31 GMT 7 | 8 | _Version update only_ 9 | 10 | ## 1.5.0 11 | Mon, 16 Aug 2021 22:23:27 GMT 12 | 13 | ### Updates 14 | 15 | - correctly creation claim so that it can be verified. Also added method to create Operator DID Document 16 | 17 | ## 1.4.0 18 | Thu, 29 Jul 2021 12:49:07 GMT 19 | 20 | _Version update only_ 21 | 22 | ## 1.3.0 23 | Fri, 18 Jun 2021 00:21:50 GMT 24 | 25 | _Version update only_ 26 | 27 | ## 1.2.0 28 | Tue, 15 Jun 2021 23:24:58 GMT 29 | 30 | _Version update only_ 31 | 32 | ## 1.1.0 33 | Tue, 04 May 2021 15:35:33 GMT 34 | 35 | ### Updates 36 | 37 | - add addClaim to DID class. Also can pass in mint amount to createDocument. 38 | - remove unused param in DID.test.ts 39 | 40 | -------------------------------------------------------------------------------- /libraries/did/jest.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | let sharedConfig = require('../../jest.config.js'); 7 | 8 | module.exports = { 9 | ...sharedConfig, 10 | rootDir: './' 11 | }; 12 | -------------------------------------------------------------------------------- /libraries/did/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@energyweb/ssi-did", 3 | "version": "0.0.1", 4 | "description": "DID operations", 5 | "homepage": "https://github.com/energywebfoundation/ssi#readme", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/energywebfoundation/ssi" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/energywebfoundation/ssi/issues" 12 | }, 13 | "main": "dist/index.js", 14 | "scripts": { 15 | "build": "rimraf dist && tsc", 16 | "test": "jest" 17 | }, 18 | "author": "Energy Web", 19 | "license": "Apache-2.0", 20 | "dependencies": { 21 | "@trust/keyto": "^1.0.1", 22 | "ethers": "6.6.2", 23 | "did-resolver": "~4.1.0", 24 | "ethr-did-resolver": "~8.0.0", 25 | "@spruceid/didkit-wasm-node": "~0.2.1", 26 | "@credo-ts/askar": "^0.5.3", 27 | "@credo-ts/core": "^0.5.3", 28 | "@credo-ts/node": "^0.5.3", 29 | "@hyperledger/aries-askar-nodejs": "^0.2.1" 30 | }, 31 | "devDependencies": { 32 | "@types/jest": "^29.5.2", 33 | "babel-jest": "29.5.0", 34 | "eslint": "^8.22.0", 35 | "jest": "^29.5.0", 36 | "rimraf": "^4.0.0", 37 | "ts-jest": "^29.1.1", 38 | "typescript": "^4.8.0", 39 | "@energyweb/eslint-config": "~0.1.0", 40 | "@energyweb/prettier-config": "~0.0.1", 41 | "@typescript-eslint/eslint-plugin": "^5.60.1", 42 | "@typescript-eslint/parser": "^5.60.1", 43 | "eslint-config-prettier": "^8.8.0", 44 | "eslint-plugin-no-only-tests": "~3.1.0", 45 | "prettier": "^2.8.8" 46 | }, 47 | "publishConfig": { 48 | "access": "public", 49 | "registry": "https://registry.npmjs.org" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /libraries/did/src/did-ethr-factory.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { DIDEthrFactory } from './did-ethr-factory'; 7 | 8 | describe('DIDEthrFactory', () => { 9 | it('should create did', async () => { 10 | // copied this JWK from https://w3c-ccg.github.io/lds-ecdsa-secp256k1-2019/#example-1 11 | const publicKeyJWK = { 12 | crv: 'secp256k1', 13 | kid: 'JUvpllMEYUZ2joO59UNui_XYDqxVqiFLLAJ8klWuPBw', 14 | kty: 'EC', 15 | x: 'dWCvM4fTdeM0KmloF57zxtBPXTOythHPMm1HCLrdd3A', 16 | y: '36uMVGM7hnw-N6GnjFcihWE3SkrhMLzzLCdPMXPEXlA' 17 | }; 18 | const didDocument = await DIDEthrFactory.generate(publicKeyJWK); 19 | const expectedDID = 'did:ethr:0x346e9a6197a01df272b873975ecbc5e190043e73'; 20 | expect(didDocument.id).toEqual(expectedDID); 21 | expect(didDocument.verificationMethod?.length).toEqual(1); 22 | expect(didDocument.verificationMethod![0].publicKeyJwk).toMatchObject(publicKeyJWK); 23 | expect(didDocument.verificationMethod![0].id).toEqual(`${expectedDID}#controller`); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /libraries/did/src/did-ethr-factory.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { DIDDocument, VerificationMethod } from 'did-resolver'; 7 | import { verificationMethodTypes } from 'ethr-did-resolver'; 8 | import { DifJsonWebKey } from '.'; 9 | import { keyToDID, keyToVerificationMethod } from '@spruceid/didkit-wasm-node'; 10 | 11 | export class DIDEthrFactory { 12 | /** 13 | * Generate a new did:ethr DID from an secp256k1 public key 14 | * @returns The default DID Document of the DID. E.g. https://github.com/decentralized-identity/ethr-did-resolver#did-document 15 | */ 16 | public static async generate(secp256k1PublicKey: DifJsonWebKey): Promise { 17 | // TODO: confirm that key passed as param is actually an secp256k1 key (or add tests to confirm that this check is being done) 18 | const did = await keyToDID('ethr', JSON.stringify(secp256k1PublicKey)); 19 | const address = did.split(':').pop(); 20 | 21 | // Format taken from https://github.com/decentralized-identity/ethr-did-resolver/blob/d6ebdfc15059b8e5c4cc8b4c3fd6a32ad3f295eb/src/resolver.ts#L261 22 | // TODO: Maybe it would be better if the ethr-did-resolver package could be used to generate the default DID document 23 | const verificationMethodId = await keyToVerificationMethod('ethr', JSON.stringify(secp256k1PublicKey)); 24 | const defaultVerificationMethod: VerificationMethod = { 25 | id: verificationMethodId, 26 | type: verificationMethodTypes.EcdsaSecp256k1RecoveryMethod2020, 27 | controller: did, 28 | blockchainAccountId: `${address}`, 29 | publicKeyJwk: secp256k1PublicKey 30 | }; 31 | 32 | return { 33 | id: did, 34 | verificationMethod: [defaultVerificationMethod] 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /libraries/did/src/did-key-factory.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { Agent, Key, KeyDidCreateOptions, KeyType, Ed25519Jwk } from "@credo-ts/core"; 7 | import { AskarModule } from '@credo-ts/askar'; 8 | import { DIDDocument } from 'did-resolver'; 9 | import { DifJsonWebKey } from '.'; 10 | 11 | export class DIDKeyFactory { 12 | /** 13 | * Generate a new did:key DID from a public JWK 14 | * Currently, only Ed25519 keys are supported 15 | * @returns The default DID Document of the DID. E.g. https://github.com/decentralized-identity/ethr-did-resolver#did-document 16 | */ 17 | public static async generate(agent: Agent<{askar: AskarModule;}>, ed25119Key: DifJsonWebKey): Promise { 18 | const ed25519Jwk = new Ed25519Jwk({ x: ed25119Key.x! }); 19 | const credoKey: Key = new Key(ed25519Jwk.publicKey, KeyType.Ed25519); 20 | 21 | const didCreateOptions: KeyDidCreateOptions = { 22 | method: 'key', 23 | options: { 24 | key: credoKey, 25 | }, 26 | }; 27 | 28 | const did = await agent.dids.create(didCreateOptions); 29 | 30 | if (did.didState.didDocument) { 31 | return did.didState.didDocument; 32 | } else { 33 | throw new Error('Error generating did'); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /libraries/did/src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | export * from './did-ethr-factory'; 7 | export * from './did-key-factory'; 8 | export * from './json-web-key'; 9 | -------------------------------------------------------------------------------- /libraries/did/src/json-web-key.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * Copied from Decentralized Identity Foundation (DIF) did-resolver because it is not exported from the package 8 | * https://github.com/decentralized-identity/did-resolver/blob/d732d6c18d9c5be8d3b810897cf05074f38788eb/src/resolver.ts#L73 9 | */ 10 | export interface DifJsonWebKey { 11 | alg?: string; 12 | crv?: string; 13 | e?: string; 14 | ext?: boolean; 15 | key_ops?: string[]; 16 | kid?: string; 17 | kty: string; 18 | n?: string; 19 | use?: string; 20 | x?: string; 21 | y?: string; 22 | } 23 | -------------------------------------------------------------------------------- /libraries/did/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist" 5 | }, 6 | "exclude": ["node_modules"], 7 | "include": ["./src/**/*.ts", "./test/**/*.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /libraries/webkms/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /libraries/webkms/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | module.exports = { 7 | parserOptions: { 8 | tsconfigRootDir: __dirname, 9 | project: 'tsconfig.json' 10 | }, 11 | extends: ['@energyweb'], 12 | env: { 13 | es2021: true, 14 | node: true, 15 | jest: true 16 | }, 17 | ignorePatterns: ['.eslintrc.js'] 18 | }; 19 | -------------------------------------------------------------------------------- /libraries/webkms/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@energyweb/w3c-ccg-webkms", 3 | "version": "0.0.1", 4 | "description": "Description of an interface to a keymanagement system", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "build": "tsc" 8 | }, 9 | "homepage": "https://github.com/energywebfoundation/ssi/#readme", 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/energywebfoundation/ssi.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/energywebfoundation/ssi/issues" 16 | }, 17 | "author": "Energy Web", 18 | "license": "Apache-2.0", 19 | "dependencies": { 20 | "ethers": "6.6.2", 21 | "jose": "^4.14.4" 22 | }, 23 | "devDependencies": { 24 | "eslint": "^8.22.0", 25 | "typescript": "^4.8.0", 26 | "@energyweb/eslint-config": "~0.1.0", 27 | "@energyweb/prettier-config": "~0.0.1", 28 | "@typescript-eslint/eslint-plugin": "^5.60.1", 29 | "@typescript-eslint/parser": "^5.60.1", 30 | "eslint-config-prettier": "^8.8.0", 31 | "eslint-plugin-no-only-tests": "~3.1.0", 32 | "prettier": "^2.8.8" 33 | }, 34 | "publishConfig": { 35 | "access": "public", 36 | "registry": "https://registry.npmjs.org" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /libraries/webkms/src/generate-key.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | import { IKeyDescription } from './key-description'; 7 | 8 | /** 9 | * An interface representing webkms generateKey 10 | * https://w3c-ccg.github.io/webkms/#generatekey-options-map 11 | */ 12 | export interface IGenerateKey { 13 | /** 14 | * Generates a new cryptographic key in the keystore. 15 | * @returns Return the key description of the key 16 | */ 17 | generateKey: (options: IGenerateKeyOptions) => Promise; 18 | } 19 | 20 | /** 21 | * An interface representing webkms generateKey options 22 | * https://w3c-ccg.github.io/webkms/#generatekey-options-map 23 | */ 24 | export interface IGenerateKeyOptions { 25 | /** 26 | * The key type. 27 | */ 28 | type: string; 29 | } 30 | -------------------------------------------------------------------------------- /libraries/webkms/src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | export * from './key-description'; 7 | export * from './generate-key'; 8 | -------------------------------------------------------------------------------- /libraries/webkms/src/key-description.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /** 7 | * An interface representing webkms key description https://w3c-ccg.github.io/webkms/#getkeydescription-options-code-lt-map-gt-code- 8 | */ 9 | export interface IKeyDescription { 10 | /** 11 | * The ID of the key. 12 | */ 13 | keyId: string; 14 | } 15 | -------------------------------------------------------------------------------- /libraries/webkms/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist" 5 | }, 6 | "exclude": ["node_modules"], 7 | "include": ["./src/**/*.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /license-header.txt: -------------------------------------------------------------------------------- 1 | Copyright 2021 - 2023 Energy Web Foundation 2 | SPDX-License-Identifier: Apache-2.0 3 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: OWF VC API Implementation 2 | site_url: https://openwallet-foundation-labs.github.io/vc-api/ 3 | theme: 4 | name: material 5 | -------------------------------------------------------------------------------- /tests/e2e/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /tests/e2e/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 - 2023 Energy Web Foundation 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | module.exports = { 7 | parser: '@typescript-eslint/parser', 8 | parserOptions: { 9 | project: 'tsconfig.json', 10 | tsconfigRootDir: __dirname, 11 | sourceType: 'module' 12 | }, 13 | plugins: ['@typescript-eslint/eslint-plugin'], 14 | extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], 15 | root: true, 16 | env: { 17 | node: true, 18 | jest: true 19 | }, 20 | ignorePatterns: ['.eslintrc.js'], 21 | rules: {} 22 | }; 23 | -------------------------------------------------------------------------------- /tests/e2e/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@energyweb/ssi-vc-api-tests-e2e", 3 | "version": "0.0.1", 4 | "main": "index.js", 5 | "scripts": { 6 | "build": "rimraf dist && tsc", 7 | "test": "jest --runInBand" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "UNLICENSED", 12 | "devDependencies": { 13 | "@energyweb/ssi-vc-api": "workspace:*", 14 | "@nestjs/common": "^10.0.4", 15 | "@nestjs/testing": "^10.0.4", 16 | "@types/jest": "^29.5.2", 17 | "@types/supertest": "^2.0.12", 18 | "@typescript-eslint/eslint-plugin": "^5.60.1", 19 | "@typescript-eslint/parser": "^5.60.1", 20 | "eslint": "^8.22.0", 21 | "eslint-config-prettier": "^8.8.0", 22 | "eslint-plugin-prettier": "^4.2.1", 23 | "jest": "^29.5.0", 24 | "prettier": "^2.8.8", 25 | "rimraf": "^4.0.0", 26 | "supertest": "^6.3.3", 27 | "ts-jest": "^29.1.1", 28 | "ts-loader": "^9.4.4", 29 | "ts-node": "^10.9.1", 30 | "tsconfig-paths": "^4.2.0", 31 | "typescript": "^4.8.0" 32 | }, 33 | "jest": { 34 | "moduleFileExtensions": [ 35 | "js", 36 | "json", 37 | "ts" 38 | ], 39 | "rootDir": "src", 40 | "testRegex": ".*\\.e2e-spec\\.ts$", 41 | "transform": { 42 | "^.+\\.(t|j)s$": "ts-jest" 43 | }, 44 | "collectCoverageFrom": [ 45 | "**/*.(t|j)s" 46 | ], 47 | "coverageDirectory": "../coverage", 48 | "testEnvironment": "node" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2017", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": false, 16 | "noImplicitAny": false, 17 | "strictBindCallApply": false, 18 | "forceConsistentCasingInFileNames": false, 19 | "noFallthroughCasesInSwitch": false, 20 | "esModuleInterop": true, 21 | "allowJs": true 22 | } 23 | } 24 | --------------------------------------------------------------------------------