├── .github └── CODEOWNERS ├── .gitignore ├── .npmrc ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── CHANGE_LOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── SECURITY.md ├── azure-pipelines.yml ├── docs └── validation API.md ├── lib ├── api_oidc_request │ ├── IRequestor.ts │ ├── IRequestorAttestation.ts │ ├── IRequestorPresentationExchange.ts │ ├── IRequestorResult.ts │ ├── Requestor.ts │ └── RequestorBuilder.ts ├── api_validation │ ├── ITokenValidator.ts │ ├── IValidationResult.ts │ ├── IdTokenTokenValidator.ts │ ├── SelfIssuedTokenValidator.ts │ ├── SiopTokenValidator.ts │ ├── ValidationSafeguards.ts │ ├── Validator.ts │ ├── ValidatorBuilder.ts │ ├── VerifiableCredentialTokenValidator.ts │ ├── VerifiablePresentationStatusReceipt.ts │ └── VerifiablePresentationTokenValidator.ts ├── error_handling │ ├── AuthenticationErrorCode.ts │ ├── ErrorHelpers.ts │ ├── RulesValidationError.ts │ └── ValidationError.ts ├── index.ts ├── input_validation │ ├── BaseIdTokenValidation.ts │ ├── CreateJwkThumbprint.ts │ ├── DidValidation.ts │ ├── DidValidationResponse.ts │ ├── IValidationResponse.ts │ ├── IdTokenValidation.ts │ ├── IdTokenValidationResponse.ts │ ├── LinkedDataCryptoSuitePublicKey.ts │ ├── SiopValidation.ts │ ├── SiopValidationResponse.ts │ ├── ValidationHelpers.ts │ ├── ValidationQueue.ts │ ├── ValidationQueueItem.ts │ ├── VerifiableCredentialValidation.ts │ ├── VerifiableCredentialValidationJsonLd.ts │ ├── VerifiableCredentialValidationResponse.ts │ ├── VerifiablePresentationValidation.ts │ └── VerifiablePresentationValidationResponse.ts ├── options │ ├── BasicValidatorOptions.ts │ ├── IExpected.ts │ ├── IValidationOptions.ts │ ├── IValidatorOptions.ts │ └── ValidationOptions.ts ├── resolver │ └── ManagedHttpResolver.ts ├── revocation │ └── IRevokedCard.ts ├── rules_model │ ├── AuthenticationModel.ts │ ├── BaseAttestationModel.ts │ ├── BaseIssuanceModel.ts │ ├── DataProviderModel.ts │ ├── EventBindingModel.ts │ ├── IdTokenAttestationModel.ts │ ├── InputClaimModel.ts │ ├── InputModel.ts │ ├── IssuanceAttestationsModel.ts │ ├── RefreshConfigurationModel.ts │ ├── RemoteKeyAuthorizationModel.ts │ ├── RemoteKeyModel.ts │ ├── RulesModel.ts │ ├── RulesPermissionModel.ts │ ├── SelfIssuedAttestationModel.ts │ ├── TransformModel.ts │ ├── TrustedIssuerModel.ts │ ├── VerifiableCredentialModel.ts │ ├── VerifiablePresentationAttestationModel.ts │ └── presentation_exchange │ │ ├── PresentationDefinitionModel.ts │ │ ├── PresentationExchangeConstraintsModel.ts │ │ ├── PresentationExchangeInputDescriptorModel.ts │ │ ├── PresentationExchangeIssuanceModel.ts │ │ └── PresentationExchangeSchemaModel.ts ├── tracing │ ├── FetchRequest.ts │ └── IFetchRequest.ts └── verifiable_credential │ ├── ClaimBag.ts │ ├── ClaimToken.ts │ ├── JsonWebSignatureToken.ts │ └── VerifiableCredentialConstants.ts ├── package-lock.json ├── package.json ├── runtest.bat ├── tests ├── ClaimToken.spec.ts ├── Credentials.ts ├── DidValidation.spec.ts ├── ErrorHelpers.spec.ts ├── FetchRequest.spec.ts ├── IdTokenAttestationModel.spec.ts ├── IdTokenValidation.spec.ts ├── InputModel.spec.ts ├── IssuanceHelpers.ts ├── JsonWebSignatureToken.spec.ts ├── KeyvaultTest.spec.ts ├── LinkedDataCryptoSuitePublicKey.spec.ts ├── LongFormDid.spec.ts ├── ManagedHttpResolver.spec.ts ├── PresentationExchange.spec.ts ├── Requestor.spec.ts ├── RequestorBuilder.spec.ts ├── RequestorHelper.ts ├── RequestorSample.spec.ts ├── ResponderHelper.ts ├── RuleProcessor.spec.ts ├── RulesModel.spec.ts ├── SiopTokenValidator.spec.ts ├── SiopValidation.spec.ts ├── SiopValidationSimulation.spec.ts ├── SiopValidationSimulation.ts ├── TestSetup.ts ├── TokenGenerator.ts ├── ValidationHelpers.spec.ts ├── ValidationSafeguards.spec.ts ├── Validator.spec.ts ├── ValidatorBuilder.spec.ts ├── VerifiableCredentialValidation.spec.ts ├── VerifiablePresentationAttestationModel.spec.ts ├── VerifiablePresentationStatusReceipt.spec.ts ├── VerifiablePresentationValidation.spec.ts ├── helpers │ ├── extendDebuggingTimeout.js │ └── reporter.js ├── jasmine.json ├── models │ ├── ITestModel.ts │ ├── PresentationDefinitionSample1.ts │ ├── RequestAttestationsNameTagOk.ts │ ├── RequestAttestationsOneSelfAssertedResponseOk .ts │ ├── RequestAttestationsOneVcSaIdtokenResponseNoIdToken.ts │ ├── RequestAttestationsOneVcSaIdtokenResponseOk.ts │ ├── RequestAttestationsOneVcSaIdtokenResponseOne.ts │ ├── RequestOneJsonLdVcResponseNoProofInVC.ts │ ├── RequestOneJsonLdVcResponseOk.ts │ ├── RequestOneJsonLdVcResponseWrongSiopDId.ts │ ├── RequestOneJsonLdVcTwoSubjectsResponseOk.ts │ ├── RequestOneVcResponseMissingId.ts │ ├── RequestOneVcResponseOk.ts │ ├── RequestTwoVcPointerToMultipleTokens.ts │ ├── RequestTwoVcResponseOk.ts │ ├── RequestTwoVcResponseOne.ts │ └── RequestTwoVcResponseRevoked.ts └── tools │ └── SiopRequest.spec.ts └── tsconfig.json /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @microsoft/decentralized-identity -------------------------------------------------------------------------------- /.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 | # Build results 19 | dist 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # local storage 34 | store/ 35 | 36 | # Compiled binary addons (https://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | .env 60 | 61 | # next.js build output 62 | .next 63 | 64 | # Common toolchain intermediate files 65 | temp 66 | 67 | # Rush files 68 | common/temp/** 69 | .rush/temp/** 70 | 71 | # Logs 72 | logs 73 | *.log 74 | npm-debug.log* 75 | yarn-debug.log* 76 | yarn-error.log* 77 | 78 | # Runtime data 79 | pids 80 | *.pid 81 | *.seed 82 | *.pid.lock 83 | 84 | # Directory for instrumented libs generated by jscoverage/JSCover 85 | lib-cov 86 | 87 | # Coverage directory used by tools like istanbul 88 | coverage 89 | 90 | # nyc test coverage 91 | .nyc_output 92 | 93 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 94 | .grunt 95 | 96 | # Bower dependency directory (https://bower.io/) 97 | bower_components 98 | 99 | # node-waf configuration 100 | .lock-wscript 101 | 102 | # Compiled binary addons (https://nodejs.org/api/addons.html) 103 | build/Release 104 | dist 105 | 106 | # Dependency directories 107 | node_modules/ 108 | jspm_packages/ 109 | 110 | # TypeScript v1 declaration files 111 | typings/ 112 | 113 | # Optional npm cache directory 114 | .npm 115 | 116 | # Optional eslint cache 117 | .eslintcache 118 | 119 | # Optional REPL history 120 | .node_repl_history 121 | 122 | # Output of 'npm pack' 123 | *.tgz 124 | 125 | # Yarn Integrity file 126 | .yarn-integrity 127 | 128 | # dotenv environment variables file 129 | .env 130 | 131 | # next.js build output 132 | .next 133 | 134 | # distribution folder 135 | dist 136 | 137 | # nunit test results 138 | nunitresults.xml 139 | 140 | # Build results 141 | [Dd]ebug/ 142 | [Dd]ebugPublic/ 143 | [Rr]elease/ 144 | [Rr]eleases/ 145 | x64/ 146 | x86/ 147 | bld/ 148 | [Bb]in/ 149 | [Oo]bj/ 150 | [Ll]og/ 151 | [Ii]ntermediate/ 152 | [Dd]rop/ 153 | [Ss]erver/sqllite3 154 | [Pp]roperties 155 | .vs 156 | .user 157 | /services/PouchDbClient/testdb 158 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry= https://pkgs.dev.azure.com/msazure/_packaging/AD-DID-Packages/npm/registry/ 2 | always-auth=true 3 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Jasmine - Current file", 11 | "cwd": "${workspaceFolder}", 12 | "program": "${workspaceFolder}/node_modules/jasmine-ts/lib/index", 13 | "args": [ 14 | "${file}" 15 | ], 16 | "skipFiles": [ 17 | "/**" 18 | ], 19 | "env": { 20 | "JASMINE_CONFIG_PATH": "${workspaceFolder}/tests/jasmine.json" 21 | }, 22 | "console": "internalConsole", 23 | "trace": true 24 | }, 25 | { 26 | "type": "node", 27 | "request": "launch", 28 | "name": "Unit Test", 29 | "program": "${workspaceFolder}/node_modules/jasmine-ts/lib/index.js", 30 | "args": [ 31 | "--config=node_modules/ad-did-services-common/tests/jasmine.json" 32 | ], 33 | "env": { 34 | "JASMINE_CONFIG_PATH": "${fileDirname}/jasmine.json" 35 | }, 36 | "console": "internalConsole", 37 | "smartStep": true 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.detectIndentation": false, 3 | "editor.tabSize": 2, 4 | "git.ignoreLimitWarning": true, 5 | "tslint.configFile": "node_modules/ad-did-services-common/tslint.json", 6 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "run-npm-build", 8 | "type": "npm", 9 | "script": "build", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | }, 14 | "problemMatcher": [ 15 | "$tsc" 16 | ] 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Problem:** 2 | The product did not do XYZ. 3 | 4 | 5 | **Solution:** 6 | Used library A to complete scenario X with quality and style. 7 | 8 | 9 | **Validation:** 10 | Please include here how it was verified that the PR works as intended. 11 | 12 | 13 | **Type of change:** 14 | - [ ] Feature work 15 | - [ ] Bug fix 16 | - [ ] Documentation 17 | - [ ] Engineering change 18 | - [ ] Test 19 | - [ ] Logging/Telemetry 20 | 21 | 22 | **Risk**: 23 | - [ ] High – Errors could cause MAJOR regression of many scenarios. (Example: new large features or high level infrastructure changes) 24 | - [ ] Medium – Errors could cause regression of 1 or more scenarios. (Example: somewhat complex bug fixes, small new features) 25 | - [ ] Small – No issues are expected. (Example: Very small bug fixes, string changes, or configuration settings changes) 26 | 27 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Node.js 2 | # Build a general Node.js project with npm. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'ubuntu-latest' 11 | 12 | steps: 13 | - task: NodeTool@0 14 | inputs: 15 | versionSpec: '>=14.15.0 <15.0.0' 16 | displayName: 'Install Node.js' 17 | 18 | - script: | 19 | npm install 20 | npm run build 21 | npm run test 22 | displayName: 'npm install, build and test' 23 | -------------------------------------------------------------------------------- /docs/validation API.md: -------------------------------------------------------------------------------- 1 | # Validation API 2 | This document is a proposal for the typescript validation API. The validation API is one of the major parts of the 3 | Relying party SDK. 4 | 5 | We use the builder design pattern for the API. This allow us to feed in different options into the validation API. 6 | 7 | For MVP the first example is needed. The other examples can be supported by the current architecture. 8 | 9 | ## Example: Validate a SIOP request 10 | 11 | This will be used for the Validation endpoint. 12 | 13 | ```javascript 14 | // Create a validator with a custom resolver. 15 | const validator = await new ValidationBuilder() 16 | .useDidResolvers(resolvers) 17 | .useHttpClient(httpClient) 18 | .continueAfterFailingValidation() // not MVP 19 | .build(); 20 | 21 | const validationResult = validator.validate(siop: string, expectedAudience: string, expectedIssuer: string); 22 | 23 | console.log(`Result: ${validationResult.ok}`); 24 | console.log(`VCs: ${JSON.stringify(validationResult.claims.vcs)}`); 25 | console.log(`Id Tokens: ${JSON.stringify(validationResult.claims.idTokens)}`); 26 | console.log(`Self issued: ${JSON.stringify(validationResult.claims.selfIssued)}`); 27 | console.log(`VCs: ${validationResult.claims.vcs[0].ok}`); 28 | console.log(`VCs: ${validationResult.claims.vcs[0].detailedError}`); 29 | console.log(`VCs claims: ${JSON.stringify(validationResult.claims.vcs[0].vc)}`); 30 | ``` 31 | 32 | 33 | ## Example: Validate a Verifiable Credential 34 | 35 | ```javascript 36 | const vcValidator = new ValidationBuilder() 37 | .inputIs(TokenType.verifiableCredential) 38 | .build(); 39 | const validationResult = await validator.validate(vc: string, expectedAudience: string, expectedIssuers: string[]); 40 | ``` 41 | 42 | ## Example: Validate a Verifiable Presentation 43 | ```javascript 44 | const vpValidator = new ValidationBuilder() 45 | .inputIs(TokenType.verifiablePresentationJwt) 46 | .build(); 47 | const validationResult = await validator.validate(vp: string, expectedAudience: string, expectedIssuer: string); 48 | ``` 49 | 50 | ## Example: Validate an id token 51 | ```javascript 52 | const idTokenValidator = new ValidationBuilder() 53 | .inputIs(TokenType.idToken) 54 | .usePublicKey(key) 55 | .build(); 56 | const validationResult = await validator.validate(idToken: string, expectedAudience: string, expectedIssuers: string[]); 57 | ``` 58 | 59 | ## Example: Validate a SIOP request with custom id token validator 60 | ```javascript 61 | const idTokenValidator = new ValidationBuilder() 62 | .inputIs(TokenType.idToken) 63 | .build(); 64 | 65 | const validator = new ValidationBuilder() 66 | .useValidators([idTokenValidator]) 67 | .build(); 68 | const validationResult = await validator.validate(siop: string, expectedAudience: string, expectedIssuer: string); 69 | ``` 70 | 71 | -------------------------------------------------------------------------------- /lib/api_oidc_request/IRequestor.ts: -------------------------------------------------------------------------------- 1 | import { IssuanceAttestationsModel } from '../index'; 2 | 3 | /** 4 | * Interface to initialize the Requestor 5 | */ 6 | export default interface IRequestor { 7 | 8 | /** 9 | * the name of the requestor (Relying Party) 10 | */ 11 | clientName: string, 12 | 13 | /** 14 | * explaining the purpose of sending claims to relying party 15 | */ 16 | clientPurpose: string, 17 | 18 | /** 19 | * the url where the request came from 20 | */ 21 | clientId: string, 22 | 23 | /** 24 | * url to send response to 25 | */ 26 | redirectUri: string, 27 | 28 | /** 29 | * url pointing to terms and service user can open in a webview 30 | */ 31 | tosUri: string, 32 | 33 | /** 34 | * url pointing to logo of the requestor (Relying Party) 35 | */ 36 | logoUri: string, 37 | } -------------------------------------------------------------------------------- /lib/api_oidc_request/IRequestorAttestation.ts: -------------------------------------------------------------------------------- 1 | import { IssuanceAttestationsModel, IRequestor } from '../index'; 2 | 3 | /** 4 | * Interface to initialize the Requestor 5 | */ 6 | export default interface IRequestorAttestation extends IRequestor { 7 | /** 8 | * claims being asked for 9 | */ 10 | attestations: IssuanceAttestationsModel 11 | } -------------------------------------------------------------------------------- /lib/api_oidc_request/IRequestorPresentationExchange.ts: -------------------------------------------------------------------------------- 1 | import { IRequestor, PresentationDefinitionModel } from '../index'; 2 | 3 | /** 4 | * Interface to initialize the Requestor 5 | */ 6 | export default interface IRequestorPresentationExchange extends IRequestor { 7 | /** 8 | * definition of the claims being asked for 9 | */ 10 | presentationDefinition: PresentationDefinitionModel 11 | } -------------------------------------------------------------------------------- /lib/api_oidc_request/IRequestorResult.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import { IValidationResult, ClaimToken } from '../index'; 6 | 7 | /** 8 | * The requestor result interface 9 | */ 10 | export interface IRequestorResult { 11 | /** 12 | * True if passed 13 | */ 14 | result: boolean; 15 | 16 | /** 17 | * Suggested Http status 18 | */ 19 | status: number; 20 | 21 | /** 22 | * Output if false. Detailed error message that can be passed in the response 23 | */ 24 | detailedError?: string; 25 | 26 | /** 27 | * The generated request for the provider 28 | */ 29 | request?: string; 30 | 31 | /** 32 | * Header that can be used for the response 33 | */ 34 | header?: {[key: string]: string}; 35 | } 36 | -------------------------------------------------------------------------------- /lib/api_oidc_request/RequestorBuilder.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import { Crypto, CryptoBuilder, Requestor, IRequestorAttestation, IRequestor } from '../index'; 6 | 7 | /** 8 | * Defines the presentation protcol 9 | */ 10 | export enum PresentationProtocol { 11 | /** 12 | * The presentation exchange protocol 13 | */ 14 | presentationExchange, 15 | 16 | /** 17 | * The attestation presentation protocol 18 | */ 19 | attestation 20 | } 21 | 22 | /** 23 | * Class to build an OIDC requestor 24 | */ 25 | export default class RequestorBuilder { 26 | 27 | private oidRequestExpirty: number = 5 * 60; 28 | private _state: string | undefined; 29 | private _nonce: string | undefined; 30 | private _issuance: boolean = false; 31 | private _crypto: Crypto = new CryptoBuilder().build(); 32 | 33 | /** 34 | * Create a new instance of RequestorBuilder 35 | * @param requestor Initializer for the requestor 36 | */ 37 | constructor(public requestor: IRequestor, crypto?: Crypto) { 38 | if (crypto) { 39 | this._crypto = crypto; 40 | } 41 | } 42 | 43 | /** 44 | * Gets the crypto object 45 | */ 46 | public get crypto() { 47 | return this._crypto; 48 | } 49 | 50 | /** 51 | * Gets the presentation protocol 52 | */ 53 | public get presentationProtocol() { 54 | return (this.requestor).attestations ? PresentationProtocol.attestation : PresentationProtocol.presentationExchange; 55 | } 56 | 57 | /** 58 | * Get the name of the requestor (Relying Party) 59 | */ 60 | public get clientName() { 61 | return this.requestor.clientName; 62 | } 63 | 64 | /** 65 | * Get the requestor's purpose for the request 66 | */ 67 | public get clientPurpose() { 68 | return this.requestor.clientPurpose; 69 | } 70 | 71 | /** 72 | * Get the url where the request came from 73 | */ 74 | public get clientId() { 75 | return this.requestor.clientId; 76 | } 77 | 78 | /** 79 | * Get the url to send response to 80 | */ 81 | public get redirectUri() { 82 | return this.requestor.redirectUri; 83 | } 84 | 85 | /** 86 | * Gets the url pointing to terms and service user can open in a webview 87 | */ 88 | public get tosUri() { 89 | return this.requestor.tosUri; 90 | } 91 | 92 | /** 93 | * Gets the url pointing to logo of the requestor (Relying Party) 94 | */ 95 | public get logoUri() { 96 | return this.requestor.logoUri; 97 | } 98 | //#endregion 99 | 100 | /** 101 | * Sets the OIDC request expiry 102 | * @param expiry The OIDC request expiry 103 | * @returns The validator builder 104 | */ 105 | public useOidcRequestExpiry(expiry: number): RequestorBuilder { 106 | this.oidRequestExpirty = expiry; 107 | return this; 108 | } 109 | 110 | /** 111 | * Gets the OIDC request expiry 112 | */ 113 | public get OidcRequestExpiry(): number { 114 | return this.oidRequestExpirty; 115 | } 116 | 117 | /** 118 | * Sets the state 119 | * @param state The state for the request 120 | * @returns The validator builder 121 | */ 122 | public useState(state: string): RequestorBuilder { 123 | this._state = state; 124 | return this; 125 | } 126 | 127 | /** 128 | * Get the state for the request 129 | */ 130 | public get state() { 131 | return this._state; 132 | } 133 | 134 | /** 135 | * Sets the allowIssuance property. 136 | * @returns The validator builder 137 | */ 138 | public allowIssuance(): RequestorBuilder { 139 | this._issuance = true; 140 | return this; 141 | } 142 | 143 | /** 144 | * Gets true if issuance is allowed 145 | */ 146 | public get issuance() { 147 | return this._issuance; 148 | } 149 | 150 | /** 151 | * Sets the nonce 152 | * @param nonce The nonce for the request 153 | * @returns The validator builder 154 | */ 155 | public useNonce(nonce: string): RequestorBuilder { 156 | this._nonce = nonce; 157 | return this; 158 | } 159 | 160 | /** 161 | * Get the nonce for the request 162 | */ 163 | public get nonce() { 164 | return this._nonce; 165 | } 166 | 167 | /** 168 | * Build the requestor 169 | */ 170 | public build(): Requestor { 171 | return new Requestor(this); 172 | } 173 | } 174 | 175 | -------------------------------------------------------------------------------- /lib/api_validation/ITokenValidator.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { TokenType } from '../index'; 7 | import { IValidationResponse } from '../input_validation/IValidationResponse'; 8 | import ValidationQueue from '../input_validation/ValidationQueue'; 9 | import ValidationQueueItem from '../input_validation/ValidationQueueItem'; 10 | 11 | /** 12 | * Interface to validate a token 13 | */ 14 | export default interface ITokenValidator { 15 | /** 16 | * Gets the type of token to validate 17 | */ 18 | isType: TokenType; 19 | 20 | /** 21 | * Validate the token 22 | * @param queue with tokens to validate 23 | * @param queueItem under validation 24 | */ 25 | validate(queue: ValidationQueue, queueItem: ValidationQueueItem, siopDid?: string, siopContract?: string): Promise; 26 | 27 | 28 | /** 29 | * Get tokens from current items and add them to the queue. 30 | * @param validationResponse The response for the requestor 31 | * @param queue with tokens to validate 32 | */ 33 | getTokens(validationResponse: IValidationResponse, queue: ValidationQueue): IValidationResponse; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /lib/api_validation/IValidationResult.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { ClaimToken, IVerifiablePresentationStatus } from '../index'; 7 | 8 | export default interface IValidationResult { 9 | /** 10 | * Gets the DID of the requestor 11 | */ 12 | did?: string, 13 | 14 | /** 15 | * Gets the contract of the requestor 16 | */ 17 | contract?: string, 18 | 19 | /** 20 | * Claims found in the input verifiable credentials 21 | */ 22 | verifiableCredentials?: { [type: string]: ClaimToken }, 23 | 24 | /** 25 | * Claims found in the input verifiable presentations 26 | */ 27 | verifiablePresentations?: { [type: string]: ClaimToken }, 28 | 29 | /** 30 | * Claims found in the input id tokens 31 | */ 32 | idTokens?: { [id: string]: ClaimToken }, 33 | 34 | /** 35 | * Claims found in the input self issued token 36 | */ 37 | selfIssued?: ClaimToken 38 | 39 | /** 40 | * Claims found in the input SIOP 41 | */ 42 | siop?: ClaimToken 43 | 44 | /** 45 | * The jti of the incoming siop token 46 | */ 47 | siopJti?: string, 48 | 49 | verifiablePresentationStatus?: { [jti: string]: IVerifiablePresentationStatus } 50 | } -------------------------------------------------------------------------------- /lib/api_validation/IdTokenTokenValidator.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { TokenType, IExpectedIdToken, ITokenValidator, ClaimToken, ValidationError } from '../index'; 7 | import { IValidationResponse } from '../input_validation/IValidationResponse'; 8 | import ValidationOptions from '../options/ValidationOptions'; 9 | import IValidatorOptions from '../options/IValidatorOptions'; 10 | import { IdTokenValidation } from '../input_validation/IdTokenValidation'; 11 | import ValidationQueue from '../input_validation/ValidationQueue'; 12 | import ValidationQueueItem from '../input_validation/ValidationQueueItem'; 13 | import ErrorHelpers from '../error_handling/ErrorHelpers'; 14 | const errorCode = (error: number) => ErrorHelpers.errorCode('VCSDKIDTV', error); 15 | 16 | /** 17 | * Class to validate a token 18 | */ 19 | export default class IdTokenTokenValidator implements ITokenValidator { 20 | 21 | /** 22 | * Create new instance of 23 | * @param validatorOption The options used during validation 24 | * @param expected values to find in the token to validate 25 | */ 26 | constructor (private validatorOption: IValidatorOptions, private expected: IExpectedIdToken) { 27 | } 28 | 29 | 30 | /** 31 | * Validate the token 32 | * @param queue with tokens to validate 33 | * @param queueItem under validation 34 | * @param siopDid Some validators wil check if the siop DID corresponds with their audience 35 | * @param siopContract Conract type asked during siop 36 | */ 37 | public async validate(_queue: ValidationQueue, queueItem:ValidationQueueItem, _siopDid: string, siopContract: string): Promise { 38 | const options = new ValidationOptions(this.validatorOption, TokenType.idToken); 39 | const validator = new IdTokenValidation(options, this.expected, siopContract); 40 | const validationResult = await validator.validate(queueItem.tokenToValidate); 41 | return validationResult as IValidationResponse; 42 | } 43 | 44 | /** 45 | * Get tokens from current item and add them to the queue. 46 | * @param validationResponse The response for the requestor 47 | * @param queue with tokens to validate 48 | */ 49 | public getTokens(_validationResponse: IValidationResponse, _queue: ValidationQueue ): IValidationResponse { 50 | throw new ValidationError(`Not implemented`, errorCode(1)); 51 | } 52 | 53 | /** 54 | * Gets the type of token to validate 55 | */ 56 | public get isType(): TokenType { 57 | return TokenType.idToken; 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /lib/api_validation/SelfIssuedTokenValidator.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import ErrorHelpers from '../error_handling/ErrorHelpers'; 7 | import { IExpectedSelfIssued, ITokenValidator, TokenType, ValidationError } from '../index'; 8 | import { IValidationResponse } from '../input_validation/IValidationResponse'; 9 | import ValidationQueue from '../input_validation/ValidationQueue'; 10 | import ValidationQueueItem from '../input_validation/ValidationQueueItem'; 11 | import IValidatorOptions from '../options/IValidatorOptions'; 12 | import ValidationOptions from '../options/ValidationOptions'; 13 | const errorCode = (error: number) => ErrorHelpers.errorCode('VCSDKSITV', error); 14 | 15 | /** 16 | * Class to validate a token 17 | */ 18 | export default class SelfIssuedTokenValidator implements ITokenValidator { 19 | 20 | /** 21 | * Create new instance of 22 | * @param validatorOption The options used during validation 23 | * @param expected values to find in the token to validate 24 | */ 25 | constructor (private validatorOption: IValidatorOptions, private expected: IExpectedSelfIssued) { 26 | } 27 | 28 | 29 | /** 30 | * Validate the token 31 | * @param queue with tokens to validate 32 | * @param queueItem under validation 33 | */ 34 | public async validate(_queue: ValidationQueue, _queueItem:ValidationQueueItem): Promise { 35 | const options = new ValidationOptions(this.validatorOption, TokenType.selfIssued); 36 | 37 | const validationResponse: IValidationResponse = { 38 | result: true, 39 | status: 200 40 | }; 41 | 42 | return validationResponse; 43 | } 44 | 45 | /** 46 | * Get tokens from current item and add them to the queue. 47 | * @param validationResponse The response for the requestor 48 | * @param queue with tokens to validate 49 | */ 50 | public getTokens(_validationResponse: IValidationResponse, _queue: ValidationQueue ): IValidationResponse { 51 | throw new ValidationError(`Not implemented`, errorCode(1)); 52 | } 53 | 54 | /** 55 | * Gets the type of token to validate 56 | */ 57 | public get isType(): TokenType { 58 | return TokenType.selfIssued; 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /lib/api_validation/ValidationSafeguards.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | /** 7 | * Interface defining all safeguards for validation 8 | */ 9 | export interface IValidationSafeguards { 10 | maxNumberOfVPTokensInSiop?: number, 11 | maxSizeOfVPTokensInSiop?: number, 12 | maxNumberOfVCTokensInPresentation?: number, 13 | maxSizeOfVCTokensInPresentation?: number, 14 | maxNumberOfIdTokensInSiop?: number, 15 | maxSizeOfIdToken?: number 16 | } 17 | 18 | /** 19 | * Class to define the safeguards used during validation of tokens 20 | */ 21 | export default class ValidationSafeguards { 22 | 23 | private _maxNumberOfVPTokensInSiop: number; 24 | private _maxSizeOfVPTokensInSiop: number; 25 | private _maxNumberOfVCTokensInPresentation: number; 26 | private _maxSizeOfVCTokensInPresentation: number; 27 | private _maxNumberOfIdTokensInSiop: number; 28 | private _maxSizeOfIdToken: number; 29 | 30 | constructor(safeguards?: IValidationSafeguards) { 31 | this._maxNumberOfVPTokensInSiop = safeguards?.maxNumberOfVPTokensInSiop || 10; 32 | this._maxSizeOfVPTokensInSiop = safeguards?.maxSizeOfVPTokensInSiop || 16 * 1024 * 1024; 33 | this._maxNumberOfVCTokensInPresentation = safeguards?.maxNumberOfVCTokensInPresentation || 1; 34 | this._maxSizeOfVCTokensInPresentation = safeguards?.maxSizeOfVCTokensInPresentation || 16 * 1024 * 1024; 35 | this._maxNumberOfIdTokensInSiop = safeguards?.maxNumberOfIdTokensInSiop || 5; 36 | this._maxSizeOfIdToken = safeguards?.maxSizeOfIdToken || 16 * 1024 * 1024; 37 | } 38 | 39 | /** 40 | * Gets the maximum number of VP tokens in a SIOP 41 | */ 42 | public get maxNumberOfVPTokensInSiop() { 43 | return this._maxNumberOfVPTokensInSiop; 44 | } 45 | 46 | /** 47 | * Sets the maximum number of VP tokens in a SIOP 48 | */ 49 | public set maxNumberOfVPTokensInSiop(value: number) { 50 | this._maxNumberOfVPTokensInSiop = value; 51 | } 52 | 53 | /** 54 | * Gets the maximum number of VP tokens in a SIOP 55 | */ 56 | public get maxNumberOfVCTokensInPresentation() { 57 | return this._maxNumberOfVCTokensInPresentation; 58 | } 59 | 60 | /** 61 | * Sets the maximum number of VP tokens in a SIOP 62 | */ 63 | public set maxNumberOfVCTokensInPresentation(value: number) { 64 | this._maxNumberOfVCTokensInPresentation = value; 65 | } 66 | 67 | /** 68 | * Gets the maximum number of VP tokens in a SIOP 69 | */ 70 | public get maxSizeOfVPTokensInSiop() { 71 | return this._maxSizeOfVPTokensInSiop; 72 | } 73 | 74 | /** 75 | * Sets the maximum number of VP tokens in a SIOP 76 | */ 77 | public set maxSizeOfVPTokensInSiop(value: number) { 78 | this._maxSizeOfVPTokensInSiop = value; 79 | } 80 | 81 | /** 82 | * Gets the maximum number of VP tokens in a SIOP 83 | */ 84 | public get maxSizeOfVCTokensInPresentation() { 85 | return this._maxSizeOfVCTokensInPresentation; 86 | } 87 | 88 | /** 89 | * Sets the maximum number of VP tokens in a SIOP 90 | */ 91 | public set maxSizeOfVCTokensInPresentation(value: number) { 92 | this._maxSizeOfVCTokensInPresentation = value; 93 | } 94 | 95 | /** 96 | * Gets the maximum number of VP tokens in a SIOP 97 | */ 98 | public get maxNumberOfIdTokensInSiop() { 99 | return this._maxNumberOfIdTokensInSiop; 100 | } 101 | 102 | /** 103 | * Sets the maximum number of VP tokens in a SIOP 104 | */ 105 | public set maxNumberOfIdTokensInSiop(value: number) { 106 | this._maxNumberOfIdTokensInSiop = value; 107 | } 108 | 109 | /** 110 | * Gets the maximum number of VP tokens in a SIOP 111 | */ 112 | public get maxSizeOfIdToken() { 113 | return this._maxSizeOfIdToken; 114 | } 115 | 116 | /** 117 | * Sets the maximum number of VP tokens in a SIOP 118 | */ 119 | public set maxSizeOfIdToken(value: number) { 120 | this._maxSizeOfIdToken = value; 121 | } 122 | } -------------------------------------------------------------------------------- /lib/api_validation/VerifiableCredentialTokenValidator.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import ErrorHelpers from '../error_handling/ErrorHelpers'; 7 | import { IExpectedVerifiableCredential, ITokenValidator, TokenType, ValidationError } from '../index'; 8 | import { IValidationResponse } from '../input_validation/IValidationResponse'; 9 | import ValidationQueue from '../input_validation/ValidationQueue'; 10 | import ValidationQueueItem from '../input_validation/ValidationQueueItem'; 11 | import { VerifiableCredentialValidation } from '../input_validation/VerifiableCredentialValidation'; 12 | import IValidatorOptions from '../options/IValidatorOptions'; 13 | import ValidationOptions from '../options/ValidationOptions'; 14 | const errorCode = (error: number) => ErrorHelpers.errorCode('VCSDKVCTV', error); 15 | 16 | /** 17 | * Class to validate a token 18 | */ 19 | export default class VerifiableCredentialTokenValidator implements ITokenValidator { 20 | 21 | /** 22 | * Create new instance of 23 | * @param validatorOption The options used during validation 24 | * @param expected values to find in the token to validate 25 | */ 26 | constructor(private validatorOption: IValidatorOptions, private expected: IExpectedVerifiableCredential) { 27 | } 28 | 29 | 30 | /** 31 | * Validate the token 32 | * @param queue with tokens to validate 33 | * @param queueItem under validation 34 | * @param siopDid needs to be equal to audience of VC 35 | */ 36 | public async validate(_queue: ValidationQueue, queueItem: ValidationQueueItem, siopDid: string): Promise { 37 | const options = new ValidationOptions(this.validatorOption, TokenType.verifiableCredential); 38 | 39 | if (typeof queueItem.tokenToValidate.rawToken === 'string') { 40 | const validator = new VerifiableCredentialValidation(options, this.expected); 41 | const validationResult = await validator.validate(queueItem.tokenToValidate.rawToken, siopDid); 42 | return validationResult as IValidationResponse; 43 | } 44 | 45 | const validator = new VerifiableCredentialValidation(options, this.expected); 46 | const validationResult = await validator.validate(queueItem.tokenToValidate.rawToken, siopDid); 47 | return validationResult as IValidationResponse; 48 | } 49 | 50 | /** 51 | * Get tokens from current item and add them to the queue. 52 | * @param validationResponse The response for the requestor 53 | * @param queue with tokens to validate 54 | */ 55 | public getTokens(_validationResponse: IValidationResponse, _queue: ValidationQueue): IValidationResponse { 56 | throw new ValidationError(`Not implemented`, errorCode(1)); 57 | } 58 | 59 | /** 60 | * Gets the type of token to validate 61 | */ 62 | public get isType(): TokenType { 63 | return TokenType.verifiableCredential; 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /lib/api_validation/VerifiablePresentationStatusReceipt.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import ErrorHelpers from '../error_handling/ErrorHelpers'; 6 | import { IValidationResponse, IValidationOptions, TokenType, ValidatorBuilder, DidValidation, IExpectedStatusReceipt, ClaimToken } from '../index'; 7 | const errorCode = (error: number) => ErrorHelpers.errorCode('VCSDKVPSR', error); 8 | 9 | export interface IVerifiablePresentationStatus { 10 | id: string; 11 | status: string; 12 | reason: string; 13 | passed: boolean; 14 | token: ClaimToken; 15 | } 16 | 17 | /** 18 | * Class to validate status receipts 19 | */ 20 | export default class VerifiablePresentationStatusReceipt { 21 | 22 | constructor(public receipts: any, private validationBuilder: ValidatorBuilder, private options: IValidationOptions, private expected: IExpectedStatusReceipt) { 23 | this._didValidation = new DidValidation(this.options, this.expected); 24 | } 25 | 26 | private _verifiablePresentationStatus: { [jti: string]: IVerifiablePresentationStatus } | undefined; 27 | private _didValidation: DidValidation; 28 | 29 | /** 30 | * Gets the result of the status 31 | */ 32 | public get verifiablePresentationStatus(): { [jti: string]: IVerifiablePresentationStatus } | undefined { 33 | return this._verifiablePresentationStatus; 34 | } 35 | 36 | /** 37 | * Gets the DID validator 38 | */ 39 | public get didValidation(): DidValidation { 40 | return this._didValidation; 41 | } 42 | 43 | /** 44 | * Sets the DID validator 45 | */ 46 | public set didValidation(validator: DidValidation) { 47 | this._didValidation = validator; 48 | } 49 | 50 | /** 51 | * The status validator 52 | */ 53 | public async validate(): Promise { 54 | // create new validator for receipt 55 | if (!this.receipts?.receipt) { 56 | return { 57 | result: false, 58 | status: this.options.validatorOptions.invalidTokenError, 59 | code: errorCode(1), 60 | detailedError: 'The status receipt is missing receipt' 61 | } 62 | } 63 | 64 | // Check each entry in the receipt 65 | this._verifiablePresentationStatus = {}; 66 | for (let jti in this.receipts.receipt) { 67 | const receipt = this.receipts.receipt[jti]; 68 | const receiptResponse = await this.didValidation.validate(receipt); 69 | if (!receiptResponse.result) { 70 | return receiptResponse; 71 | } 72 | 73 | // aud should correspond requestor 74 | if (receiptResponse.payloadObject.aud !== this.expected.didAudience) { 75 | return { 76 | result: false, 77 | status: this.options.validatorOptions.invalidTokenError, 78 | detailedError: `The status receipt aud '${receiptResponse.payloadObject.aud}' is wrong. Expected '${this.expected.didAudience}'` 79 | } 80 | } 81 | 82 | // iss should correspond issuer VC 83 | if (receiptResponse.issuer !== this.expected.didIssuer) { 84 | return { 85 | result: false, 86 | status: this.options.validatorOptions.invalidTokenError, 87 | code: errorCode(2), 88 | detailedError: `The status receipt iss '${receiptResponse.issuer}' is wrong. Expected '${this.expected.didIssuer}'` 89 | } 90 | } 91 | 92 | this._verifiablePresentationStatus[jti] = { 93 | id: jti, 94 | reason: receiptResponse.payloadObject.credentialStatus?.reason, 95 | status: receiptResponse.payloadObject.credentialStatus?.status, 96 | passed: receiptResponse.payloadObject.credentialStatus?.status?.toLowerCase() === 'valid', 97 | token: new ClaimToken(TokenType.verifiablePresentationStatus, receipt) 98 | }; 99 | 100 | if (!this.verifiablePresentationStatus![jti].passed) { 101 | return { 102 | result: false, 103 | status: this.options.validatorOptions.invalidTokenError, 104 | code: errorCode(3), 105 | detailedError: `The status receipt for jti '${jti}' failed with status ${this.verifiablePresentationStatus![jti].status}.` 106 | } 107 | } 108 | } 109 | 110 | return { 111 | result: true, 112 | status: 200, 113 | validationResult: { verifiablePresentationStatus: this.verifiablePresentationStatus } 114 | }; 115 | } 116 | } -------------------------------------------------------------------------------- /lib/error_handling/AuthenticationErrorCode.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | export enum AuthenticationErrorCode { 6 | /** 7 | * Error code for a missing or malformed token 8 | * Unable to validate a token 9 | * https://tools.ietf.org/html/rfc6750#section-3.1 10 | */ 11 | invalidRequest = 'invalid_request', 12 | 13 | /** 14 | * Error code for a invalid (e.g. expired, revoked) token 15 | * Inflection point is that there was a valid attempt to validate a token 16 | * https://tools.ietf.org/html/rfc6750#section-3.1 17 | */ 18 | invalidToken = 'invalid_token', 19 | } 20 | 21 | export enum AuthenticationErrorDescription { 22 | /** 23 | * When an expected token is malformed such as a jwt that is not a jwt or a json that does not parse 24 | */ 25 | malformedToken = 'The token is malformed', 26 | } 27 | -------------------------------------------------------------------------------- /lib/error_handling/ErrorHelpers.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | export default class ErrorHelpers { 7 | /** 8 | * 9 | * @param prefix for the error code 10 | * @param errorNumber for the error code 11 | * @param size of the errorNumber, defaults to two digits 12 | */ 13 | public static errorCode(prefix: string, errorNumber: number, size: number = 2): string { 14 | const paddedErrorNumber = (errorNumber + '').padStart(size, '0'); 15 | return prefix + paddedErrorNumber; 16 | } 17 | } -------------------------------------------------------------------------------- /lib/error_handling/RulesValidationError.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | /** 7 | * Typed error for rules validation failures. 8 | */ 9 | export class RulesValidationError extends Error {} 10 | -------------------------------------------------------------------------------- /lib/error_handling/ValidationError.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | /** 7 | * Typed error for validation failures. 8 | */ 9 | export default class ValidationError extends Error { 10 | /** 11 | * Create a new instance of ValidationError 12 | * @param message describing the error message 13 | * @param code unique code the error 14 | */ 15 | constructor(message: string, public code: string) { 16 | super(message); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/input_validation/BaseIdTokenValidation.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import { IExpectedAudience } from '../options/IExpected'; 6 | import { IValidationOptions } from '../options/IValidationOptions'; 7 | import ClaimToken from '../verifiable_credential/ClaimToken'; 8 | import { IdTokenValidationResponse, IIdTokenValidation } from './IdTokenValidationResponse'; 9 | 10 | /** 11 | * Class for id token validation 12 | */ 13 | export abstract class BaseIdTokenValidation implements IIdTokenValidation { 14 | /** 15 | * Create a new instance of @see 16 | * @param options Options to steer the validation process 17 | * @param expectedAudience IExpectedAudience instance 18 | */ 19 | constructor(protected options: IValidationOptions, private expectedAudience: IExpectedAudience) { 20 | } 21 | 22 | /** 23 | * Validate the id token 24 | * @param idToken The presentation to validate as a signed token 25 | * @param siopDid needs to be equal to audience of VC 26 | */ 27 | public async validate(idToken: ClaimToken): Promise { 28 | let validationResponse: IdTokenValidationResponse = { 29 | result: true, 30 | detailedError: '', 31 | status: 200 32 | }; 33 | 34 | // Deserialize id token token 35 | validationResponse = await this.options.getTokenObjectDelegate(validationResponse, idToken.rawToken); 36 | if (!validationResponse.result) { 37 | return validationResponse; 38 | } 39 | 40 | validationResponse = await this.downloadConfigurationAndValidate(validationResponse, idToken); 41 | 42 | if (!validationResponse.result) { 43 | return validationResponse; 44 | } 45 | 46 | // Check token time validity 47 | validationResponse = await this.options.checkTimeValidityOnTokenDelegate(validationResponse); 48 | 49 | if (!validationResponse.result) { 50 | return validationResponse; 51 | } 52 | 53 | // Check token scope (aud and iss) 54 | validationResponse = await this.options.checkScopeValidityOnIdTokenDelegate(validationResponse, this.expectedAudience); 55 | 56 | if (!validationResponse.result) { 57 | return validationResponse; 58 | } 59 | 60 | return validationResponse; 61 | } 62 | 63 | protected abstract downloadConfigurationAndValidate(validationResponse: IdTokenValidationResponse, idToken: ClaimToken): Promise; 64 | } 65 | -------------------------------------------------------------------------------- /lib/input_validation/CreateJwkThumbprint.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import base64url from 'base64url'; 7 | import { createHash } from 'crypto'; 8 | 9 | /** 10 | * for a given json web key calculate the thumbprint as defined by RFC 7638 11 | * @param jwk json web key instance 12 | * @returns thumbprint 13 | */ 14 | export function createJwkThumbprint(jwk: { [key: string]: string }): string { 15 | const key = { 16 | crv: jwk.crv, 17 | e: jwk.e, 18 | kty: jwk.kty, 19 | n: jwk.n, 20 | x: jwk.x, 21 | y: jwk.y, 22 | }; 23 | 24 | const preimage = JSON.stringify(key); 25 | const digest = createHash('sha256').update(preimage, 'utf8').digest(); 26 | return base64url(digest); 27 | } 28 | -------------------------------------------------------------------------------- /lib/input_validation/DidValidation.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { IPayloadProtectionSigning } from 'verifiablecredentials-crypto-sdk-typescript'; 7 | import { AuthenticationErrorCode } from '../error_handling/AuthenticationErrorCode'; 8 | import ErrorHelpers from '../error_handling/ErrorHelpers'; 9 | import { IExpectedBase } from '../index'; 10 | import { IValidationOptions } from '../options/IValidationOptions'; 11 | import { IDidValidation, IDidValidationResponse } from './DidValidationResponse'; 12 | const errorCode = (error: number) => ErrorHelpers.errorCode('VCSDKDIDV', error); 13 | 14 | /** 15 | * Class for input validation of a token signed with DID key 16 | */ 17 | export class DidValidation implements IDidValidation { 18 | 19 | /** 20 | * Create a new instance of @see 21 | * @param options Options to steer the validation process 22 | * @param expectedSchema Expected schema of the verifiable credential 23 | */ 24 | constructor (private options: IValidationOptions, private expected: IExpectedBase) { 25 | } 26 | 27 | /** 28 | * Validate the token for a correct format and signature 29 | * @param token Token to validate 30 | * @returns true if validation passes together with parsed objects 31 | */ 32 | public async validate (token: string | object): Promise { 33 | let validationResponse: IDidValidationResponse = { 34 | result: true, 35 | status: 200, 36 | }; 37 | 38 | // Deserialize the token 39 | validationResponse = await this.options.getTokenObjectDelegate(validationResponse, token); 40 | if (!validationResponse.result) { 41 | return validationResponse; 42 | } 43 | 44 | // Get did from kid 45 | const parts = validationResponse.didKid!.split('#'); 46 | if (parts.length <= 1) { 47 | return { 48 | result: false, 49 | code: errorCode(1), 50 | detailedError: `The kid in the protected header does not contain the DID. Required format for kid is #kid`, 51 | status: this.options.validatorOptions.invalidTokenError, 52 | wwwAuthenticateError: AuthenticationErrorCode.invalidRequest, 53 | }; 54 | } 55 | validationResponse.did = parts[0]; 56 | // Get DID from the payload 57 | if (!validationResponse.did) { 58 | return validationResponse = { 59 | result: false, 60 | code: errorCode(2), 61 | detailedError: 'The kid does not contain the DID', 62 | status: this.options.validatorOptions.invalidTokenError, 63 | wwwAuthenticateError: AuthenticationErrorCode.invalidRequest, 64 | }; 65 | } 66 | 67 | // Resolve DID, get document and retrieve public key 68 | validationResponse = await this.options.resolveDidAndGetKeysDelegate(validationResponse); 69 | if (!validationResponse.result) { 70 | return validationResponse; 71 | } 72 | 73 | // Validate DID signature 74 | validationResponse = await this.options.validateDidSignatureDelegate(validationResponse, validationResponse.didSignature as IPayloadProtectionSigning ); 75 | if (!validationResponse.result) { 76 | return validationResponse; 77 | } 78 | 79 | // Check token time validity 80 | validationResponse = await this.options.checkTimeValidityOnTokenDelegate(validationResponse); 81 | if (!validationResponse.result) { 82 | return validationResponse; 83 | } 84 | 85 | // once the token is validated, we can trust the jti 86 | validationResponse.tokenId = validationResponse.payloadObject.jti || validationResponse.payloadObject.id; 87 | 88 | return validationResponse; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/input_validation/DidValidationResponse.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { IValidationResponse } from "./IValidationResponse"; 7 | 8 | export interface IDidValidationResponse extends IValidationResponse { 9 | } 10 | 11 | /** 12 | * Interface for DID validation 13 | */ 14 | export interface IDidValidation { 15 | /** 16 | * Validate the token for a correct format 17 | * @param siop Authentication of requestor 18 | * @param audience to validate 19 | * @returns true if validation passes 20 | */ 21 | validate(token: any, audience: string): Promise; 22 | } 23 | -------------------------------------------------------------------------------- /lib/input_validation/IValidationResponse.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import { IPayloadProtectionSigning } from 'verifiablecredentials-crypto-sdk-typescript'; 6 | import { DidDocument } from '@decentralized-identity/did-common-typescript'; 7 | import { IValidationResult, ClaimToken } from '../index'; 8 | 9 | /** 10 | * The response interface 11 | */ 12 | export interface IResponse { 13 | /** 14 | * True if passed 15 | */ 16 | result: boolean; 17 | 18 | /** 19 | * Http status 20 | */ 21 | status: number; 22 | 23 | /** 24 | * Output if false. Detailed error message that can be passed in the response 25 | */ 26 | detailedError?: string; 27 | 28 | /** 29 | * Output if false. Unique code for the response 30 | */ 31 | code?: string; 32 | 33 | /** 34 | * Additional error object 35 | */ 36 | innerError?: any; 37 | } 38 | 39 | export interface IValidationResponse extends IResponse { 40 | /** 41 | * The signed token 42 | */ 43 | didSignature?: IPayloadProtectionSigning; 44 | 45 | /** 46 | * The payload object 47 | */ 48 | payloadObject?: any; 49 | 50 | /** 51 | * The DID 52 | */ 53 | did?: string; 54 | 55 | /** 56 | * The DID kid 57 | */ 58 | didKid?: string; 59 | 60 | /** 61 | * The request DID 62 | */ 63 | didDocument?: DidDocument; 64 | 65 | /** 66 | * The DID signing public key 67 | */ 68 | didSigningPublicKey?: any; 69 | 70 | /** 71 | * List of tokens that still need to be validated 72 | */ 73 | tokensToValidate?: { [key: string]: ClaimToken }; 74 | 75 | /** 76 | * flag indicating whether or not tokens have been populated 77 | */ 78 | tokensArePopulated?: boolean; 79 | 80 | /** 81 | * All claims found in input tokens 82 | */ 83 | validationResult?: IValidationResult; 84 | 85 | /** 86 | * the Json Web Token Id of the incoming token 87 | */ 88 | tokenId?: string; 89 | 90 | /** 91 | * The used payload protection protocol 92 | */ 93 | payloadProtectionProtocol?: string; 94 | 95 | /** 96 | * The issuer of the token 97 | */ 98 | issuer?: string; 99 | 100 | /** 101 | * The epoch expiration time 102 | */ 103 | expiration?: number; 104 | 105 | /** 106 | * Error for the WWW-Authenticate header error field 107 | */ 108 | wwwAuthenticateError?: string; 109 | } 110 | -------------------------------------------------------------------------------- /lib/input_validation/IdTokenValidation.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import { IExpectedIdToken } from '../options/IExpected'; 6 | import { IdTokenValidationResponse } from './IdTokenValidationResponse'; 7 | import { IValidationOptions } from '../options/IValidationOptions'; 8 | import ClaimToken, { TokenType } from '../verifiable_credential/ClaimToken'; 9 | import { BaseIdTokenValidation } from './BaseIdTokenValidation'; 10 | import ErrorHelpers from '../error_handling/ErrorHelpers'; 11 | const errorCode = (error: number) => ErrorHelpers.errorCode('VCSDKIDVa', error); 12 | 13 | /** 14 | * Class for id token validation for the Verifiable Credential attestation scenario 15 | */ 16 | export class IdTokenValidation extends BaseIdTokenValidation { 17 | /** 18 | * Create a new instance of @see 19 | * @param options Options to steer the validation process 20 | * @param expected Expected properties of the id token 21 | * @param siopContract Conract type asked during siop 22 | */ 23 | constructor(options: IValidationOptions, private expected: IExpectedIdToken, private siopContract: string) { 24 | super(options, expected); 25 | } 26 | 27 | /** 28 | * Return expected issuers for id tokens 29 | * @param expected Could be a contract based object or just an array with expected issuers 30 | * @param siopContract The contract to which issuers are linked 31 | */ 32 | private getIssuersFromExpected(): string[] | IdTokenValidationResponse { 33 | if (!this.expected.configuration) { 34 | return { 35 | result: false, 36 | status: 500, 37 | code: errorCode(1), 38 | detailedError: `Expected should have configuration issuers set for idToken` 39 | }; 40 | } 41 | 42 | let issuers: string[]; 43 | 44 | // Expected can provide a list of configuration or a list linked to a contract 45 | if (this.expected.configuration instanceof Array) { 46 | if (this.expected.configuration.length === 0) { 47 | return { 48 | result: false, 49 | status: 500, 50 | code: errorCode(2), 51 | detailedError: `Expected should have configuration issuers set for idToken. Empty array presented.` 52 | }; 53 | } 54 | issuers = this.expected.configuration; 55 | } else { 56 | if (!this.siopContract) { 57 | return { 58 | result: false, 59 | status: 500, 60 | code: errorCode(3), 61 | detailedError: `The siopContract needs to be specified to validate the idTokens.` 62 | }; 63 | } 64 | 65 | // check for issuers for the contract 66 | if (!(<{ [contract: string]: string[] }>this.expected.configuration)[this.siopContract]) { 67 | return { 68 | result: false, 69 | status: 500, 70 | code: errorCode(4), 71 | detailedError: `Expected should have configuration issuers set for idToken. Missing configuration for '${this.siopContract}'.` 72 | }; 73 | } 74 | issuers = this.expected.configuration[this.siopContract] 75 | } 76 | return issuers; 77 | } 78 | 79 | protected downloadConfigurationAndValidate(validationResponse: IdTokenValidationResponse, idToken: ClaimToken): Promise { 80 | // Validate token signature 81 | const issuers = this.getIssuersFromExpected(); 82 | if (!(issuers instanceof Array)) { 83 | return Promise.resolve(issuers); 84 | } 85 | 86 | // Make sure that the IdToken endpoint matches a trusted issuer. 87 | const { id } = idToken; 88 | if (issuers.indexOf(id) < 0) { 89 | return Promise.resolve({ 90 | code: errorCode(5), 91 | detailedError: `'${id}' is not configured as a trusted OpenID Provider.`, 92 | result: false, 93 | status: 403, 94 | }); 95 | } 96 | 97 | return this.options.fetchKeyAndValidateSignatureOnIdTokenDelegate(validationResponse, idToken); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/input_validation/IdTokenValidationResponse.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import ClaimToken from '../verifiable_credential/ClaimToken'; 7 | import { IValidationResponse } from './IValidationResponse'; 8 | 9 | export interface IdTokenValidationResponse extends IValidationResponse { 10 | 11 | /** 12 | * The expected issuer of the token extracted from token configuration 13 | */ 14 | expectedIssuer?: string; 15 | } 16 | 17 | 18 | /** 19 | * Interface for id token validation 20 | */ 21 | export interface IIdTokenValidation { 22 | 23 | /** 24 | * Validate the id token 25 | * @param idToken The presentation to validate as a signed token 26 | * @param audience The expected audience for the token 27 | * @returns true if validation passes 28 | */ 29 | validate(idToken: ClaimToken): Promise; 30 | } 31 | -------------------------------------------------------------------------------- /lib/input_validation/SiopValidationResponse.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import ClaimBag from "../verifiable_credential/ClaimBag"; 7 | import { IValidationResponse } from "./IValidationResponse"; 8 | 9 | export interface ISiopValidationResponse extends IValidationResponse { 10 | 11 | /** 12 | * The contract 13 | */ 14 | contract?: string; 15 | 16 | /** 17 | * Claim in the request and rule file 18 | */ 19 | claimBag?: ClaimBag; 20 | } 21 | 22 | /** 23 | * Interface for input validation 24 | */ 25 | export interface ISiopValidation { 26 | /** 27 | * Validate the input for a correct format 28 | * @param siop Authentication of requestor 29 | * @param audience to validate 30 | * @returns true if validation passes 31 | */ 32 | validate(siop: any, audience: string): Promise; 33 | } 34 | -------------------------------------------------------------------------------- /lib/input_validation/ValidationQueue.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import ValidationQueueItem from './ValidationQueueItem'; 7 | import { IValidationResponse } from './IValidationResponse'; 8 | import ClaimToken, { TokenType } from '../verifiable_credential/ClaimToken'; 9 | 10 | export default class ValidationQueue { 11 | 12 | /** 13 | * Keeps track of all tokens to validate and the results of validated tokens 14 | */ 15 | private queue: ValidationQueueItem[] = []; 16 | 17 | /** 18 | * Add token to validation queue 19 | * @param token to add to queue 20 | */ 21 | public enqueueToken(id: string, token: ClaimToken) { 22 | this.queue.push(new ValidationQueueItem(id, token)); 23 | } 24 | 25 | 26 | /** 27 | * Add token to validation queue 28 | * @param node item to add to queue 29 | */ 30 | public enqueueItem(node: ValidationQueueItem){ 31 | this.queue.push(node); 32 | } 33 | 34 | /** 35 | * Gets the queue 36 | * @param token to add to queue 37 | */ 38 | public get items(): ValidationQueueItem[] { 39 | return this.queue; 40 | } 41 | 42 | /** 43 | * Get next token to validate from the queue 44 | */ 45 | public getNextToken(): ValidationQueueItem | undefined { 46 | for (let inx = 0 ; inx < this.queue.length; inx ++) { 47 | if (!this.queue[inx].isValidated) { 48 | return this.queue[inx]; 49 | } 50 | } 51 | // No more tokens to validate 52 | return undefined; 53 | } 54 | 55 | /** 56 | * Get the result of the validation 57 | */ 58 | public getResult(): IValidationResponse { 59 | for (let inx = this.queue.length - 1 ; inx >= 0; inx --) { 60 | const item = this.queue[inx]; 61 | if (!item.result) { 62 | return item.validationResponse; 63 | } 64 | } 65 | 66 | // No failures. Return last item 67 | return this.queue[this.queue.length - 1].validationResponse; 68 | } 69 | } -------------------------------------------------------------------------------- /lib/input_validation/ValidationQueueItem.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import ErrorHelpers from '../error_handling/ErrorHelpers'; 7 | import { ClaimToken } from '../index'; 8 | import { IValidationResponse } from './IValidationResponse'; 9 | const errorCode = (error: number) => ErrorHelpers.errorCode('VCSDKQuIt', error); 10 | 11 | export enum ValidationStatus { 12 | /** 13 | * Item needs validation 14 | */ 15 | todo = 'todo', 16 | 17 | /** 18 | * Item is validated 19 | */ 20 | validated = 'validated', 21 | 22 | /** 23 | * Item is under validated 24 | */ 25 | underValidation = 'underValidation', 26 | } 27 | 28 | export default class ValidationQueueItem { 29 | private _validationResult: IValidationResponse; 30 | private _validatedToken: ClaimToken | undefined; 31 | private validationStatus: ValidationStatus = ValidationStatus.todo; 32 | 33 | constructor(private _id: string, private _tokenToValidate: ClaimToken) { 34 | // Set defaults for validation result 35 | this._validationResult = { 36 | result: false, 37 | status: 500, 38 | code: errorCode(1), 39 | detailedError: 'Token not validated' 40 | }; 41 | } 42 | 43 | /** 44 | * Keep track of the result of the validation 45 | * @param result of the validation 46 | */ 47 | public setResult(result: IValidationResponse, token: ClaimToken) { 48 | this._validationResult = result; 49 | this._validatedToken = token; 50 | this.validationStatus = ValidationStatus.validated; 51 | } 52 | 53 | /** 54 | * Gets the validation response 55 | */ 56 | public get validationResponse() { 57 | return this._validationResult; 58 | } 59 | 60 | /** 61 | * Gets the token id 62 | */ 63 | public get id() { 64 | return this._id; 65 | } 66 | 67 | /** 68 | * Token to validate 69 | */ 70 | public get tokenToValidate(): ClaimToken { 71 | return this._tokenToValidate; 72 | } 73 | 74 | /** 75 | * Validated token 76 | */ 77 | public get validatedToken(): ClaimToken | undefined { 78 | return this._validatedToken; 79 | } 80 | 81 | /** 82 | * Return false if the token still needs to be validated or the validation failed 83 | */ 84 | public get result(): boolean { 85 | return this.isValidated && this._validationResult.result; 86 | } 87 | 88 | /** 89 | * True if the queue item has been validated 90 | */ 91 | public get isValidated(): boolean { 92 | return this.validationStatus === ValidationStatus.validated; 93 | } 94 | 95 | /** 96 | * True if the queue item is under validation 97 | */ 98 | public get isUnderValidation(): boolean { 99 | return this.validationStatus === ValidationStatus.underValidation; 100 | } 101 | } -------------------------------------------------------------------------------- /lib/input_validation/VerifiableCredentialValidationResponse.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { IValidationResponse } from './IValidationResponse'; 7 | 8 | export interface VerifiableCredentialValidationResponse extends IValidationResponse { 9 | /** 10 | * Verifiable Credential subject did. 11 | */ 12 | subject?: string; 13 | } 14 | 15 | /** 16 | * Interface for verifiable credential validation 17 | */ 18 | export interface IVerifiableCredentialValidation { 19 | 20 | /** 21 | * Validate the verifiable credential 22 | * @param verifiableCredential The credential to validate as a signed token 23 | * @param siopDid needs to be equal to audience of VC 24 | * @param siopContract Conract type asked during siop 25 | * @returns true if validation passes 26 | */ 27 | validate(verifiableCredential: string | object, siopDid: string, siopContract: string): Promise; 28 | } -------------------------------------------------------------------------------- /lib/input_validation/VerifiablePresentationValidation.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import { VerifiablePresentationValidationResponse, IVerifiablePresentationValidation } from './VerifiablePresentationValidationResponse'; 6 | import { IValidationOptions } from '../options/IValidationOptions'; 7 | import { DidValidation } from './DidValidation'; 8 | import VerifiableCredentialConstants from '../verifiable_credential/VerifiableCredentialConstants'; 9 | import { IExpectedVerifiablePresentation } from '../index'; 10 | import ErrorHelpers from '../error_handling/ErrorHelpers'; 11 | const errorCode = (error: number) => ErrorHelpers.errorCode('VCSDKVPVa', error); 12 | 13 | require('es6-promise').polyfill(); 14 | require('isomorphic-fetch'); 15 | 16 | /** 17 | * Class for verifiable presentation validation 18 | */ 19 | export class VerifiablePresentationValidation implements IVerifiablePresentationValidation { 20 | 21 | /** 22 | * Create a new instance of @see 23 | * @param options Options to steer the validation process 24 | * @param expected Expected properties of the verifiable presentation 25 | * @param siopDid needs to be equal to audience of VC 26 | */ 27 | constructor(private options: IValidationOptions, private expected: IExpectedVerifiablePresentation, private siopDid: string, private id: string) { 28 | } 29 | 30 | /** 31 | * Validate the verifiable presentation 32 | * @param verifiablePresentationToken The presentation to validate as a signed token 33 | * @param siopDid The did which presented the siop 34 | * @returns result is true if validation passes 35 | */ 36 | public async validate(verifiablePresentationToken: string): Promise { 37 | let validationResponse: VerifiablePresentationValidationResponse = { 38 | result: true, 39 | detailedError: '', 40 | status: 200 41 | }; 42 | 43 | // Check the DID parts of the VP 44 | const didValidation = new DidValidation(this.options, this.expected); 45 | validationResponse = await didValidation.validate(verifiablePresentationToken); 46 | if (!validationResponse.result) { 47 | return validationResponse; 48 | } 49 | 50 | // Check token scope (aud and iss) 51 | validationResponse = await this.options.checkScopeValidityOnVpTokenDelegate(validationResponse, this.expected, this.siopDid); 52 | if (!validationResponse.result) { 53 | return validationResponse; 54 | } 55 | 56 | // Check if VP and SIOP DID are equal 57 | if (this.siopDid && validationResponse.did !== this.siopDid) { 58 | return { 59 | result: false, 60 | code: errorCode(1), 61 | detailedError: `The DID used for the SIOP ${this.siopDid} is not equal to the DID used for the verifiable presentation ${validationResponse.did}`, 62 | status: this.options.validatorOptions.invalidTokenError 63 | }; 64 | } 65 | 66 | if (!validationResponse.payloadObject.vp) { 67 | return { 68 | result: false, 69 | status: this.options.validatorOptions.invalidTokenError, 70 | code: errorCode(2), 71 | detailedError: `Missing vp in presentation` 72 | }; 73 | } 74 | 75 | if (!validationResponse.payloadObject.vp['@context']) { 76 | return { 77 | result: false, 78 | status: this.options.validatorOptions.invalidTokenError, 79 | code: errorCode(3), 80 | detailedError: `Missing @context in presentation` 81 | }; 82 | } 83 | 84 | if (!validationResponse.payloadObject.vp.type || validationResponse.payloadObject.vp.type[0] !== VerifiableCredentialConstants.DEFAULT_VERIFIABLEPRESENTATION_TYPE) { 85 | return { 86 | result: false, 87 | status: this.options.validatorOptions.invalidTokenError, 88 | code: errorCode(3), 89 | detailedError: `Missing or wrong default type in vp of presentation. Should be ${VerifiableCredentialConstants.DEFAULT_VERIFIABLEPRESENTATION_TYPE}` 90 | }; 91 | } 92 | if (!validationResponse.payloadObject.vp['verifiableCredential']) { 93 | return { 94 | result: false, 95 | status: this.options.validatorOptions.invalidTokenError, 96 | code: errorCode(4), 97 | detailedError: `Missing verifiableCredential in presentation` 98 | }; 99 | } 100 | return validationResponse; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /lib/input_validation/VerifiablePresentationValidationResponse.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { IValidationResponse } from './IValidationResponse'; 7 | 8 | export interface VerifiablePresentationValidationResponse extends IValidationResponse { 9 | } 10 | 11 | /** 12 | * Interface for verifiable presentation validation 13 | */ 14 | export interface IVerifiablePresentationValidation { 15 | 16 | /** 17 | * Validate the verifiable presentation 18 | * @param verifiablePresentation The presentation to validate as a signed token 19 | * @param siopDid The did which presented the siop 20 | * @returns true if validation passes 21 | */ 22 | validate(verifiablePresentationToken: string, siopDid?: string): Promise; 23 | } 24 | -------------------------------------------------------------------------------- /lib/options/BasicValidatorOptions.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import { Crypto, CryptoBuilder, FetchRequest, IDidResolver, IFetchRequest, ValidationSafeguards, ValidatorBuilder } from '../index'; 6 | import IValidatorOptions from './IValidatorOptions'; 7 | 8 | /** 9 | * Basic IValidatorOptions implementation 10 | */ 11 | export default class BasicValidatorOptions implements IValidatorOptions { 12 | 13 | private readonly _crypto: Crypto; 14 | private readonly _fetchRequest: IFetchRequest; 15 | private readonly _validationSafeguards: ValidationSafeguards; 16 | private readonly _invalidTokenError: number; 17 | private readonly _performFullSiopValidation: boolean; 18 | 19 | constructor(private _resolver?: IDidResolver, performFullSiopValidation: boolean = false) { 20 | this._crypto = new CryptoBuilder().build(); 21 | this._fetchRequest = new FetchRequest(); 22 | this._validationSafeguards = new ValidationSafeguards(); 23 | this._invalidTokenError = ValidatorBuilder.INVALID_TOKEN_STATUS_CODE; 24 | this._performFullSiopValidation = performFullSiopValidation; 25 | } 26 | 27 | get performFullSiopValidation(): boolean{ 28 | return this._performFullSiopValidation; 29 | } 30 | 31 | get invalidTokenError(): number { 32 | return this._invalidTokenError; 33 | } 34 | 35 | get resolver(): IDidResolver { 36 | return this._resolver!; 37 | } 38 | 39 | /** 40 | * The fetch client 41 | */ 42 | get validationSafeguards(): ValidationSafeguards { 43 | return this._validationSafeguards; 44 | } 45 | 46 | /** 47 | * The fetch client 48 | */ 49 | get fetchRequest(): IFetchRequest { 50 | return this._fetchRequest; 51 | } 52 | 53 | get crypto(): Crypto { 54 | return this._crypto; 55 | } 56 | } -------------------------------------------------------------------------------- /lib/options/IExpected.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { TokenType } from '../index'; 7 | 8 | /** 9 | * Type issuer mapping 10 | */ 11 | export type IssuerMap = ({ [contract: string]: string[]}) | string[]; 12 | 13 | 14 | 15 | /** 16 | * Expected base 17 | */ 18 | export interface IExpectedBase { 19 | 20 | /** 21 | * The token type 22 | */ 23 | type: TokenType, 24 | } 25 | 26 | 27 | /** 28 | * Expected values for SIOP 29 | */ 30 | export interface IExpectedSiop extends IExpectedBase { 31 | 32 | /** 33 | * Expected audience url to where the siop was presented 34 | */ 35 | audience?: string, 36 | 37 | /** 38 | * Expected state in the siop 39 | */ 40 | state?: string, 41 | 42 | /** 43 | * Expected nonce in the siop 44 | */ 45 | nonce?: string 46 | } 47 | 48 | /** 49 | * Expected values for verifiable presentation 50 | */ 51 | export interface IExpectedVerifiablePresentation extends IExpectedBase { 52 | 53 | /** 54 | * Expected audience DID of the receiver of the siop 55 | */ 56 | didAudience: string 57 | } 58 | 59 | /** 60 | * Expected values for status receipts 61 | */ 62 | export interface IExpectedStatusReceipt extends IExpectedBase { 63 | 64 | /** 65 | * Expected audience DID for the receipt 66 | */ 67 | didAudience: string, 68 | 69 | /** 70 | * Expected issuer DID of the receipt 71 | */ 72 | didIssuer: string 73 | 74 | } 75 | 76 | /** 77 | * Expected values for verifiable credentials 78 | */ 79 | export interface IExpectedVerifiableCredential extends IExpectedBase { 80 | /** 81 | * Expected issuers for the different contracts. 82 | */ 83 | contractIssuers?: IssuerMap, 84 | } 85 | 86 | /** 87 | * Expected values for self issued tokens 88 | */ 89 | export interface IExpectedSelfIssued extends IExpectedBase { 90 | } 91 | 92 | export interface IExpectedAudience { 93 | 94 | /** 95 | * Expected audience for the token type 96 | */ 97 | audience?: string 98 | } 99 | 100 | 101 | /** 102 | * Expected values for id tokens 103 | */ 104 | export interface IExpectedIdToken extends IExpectedBase, IExpectedAudience { 105 | /** 106 | * Expected issuers configuration endpoint for the different contracts. 107 | */ 108 | configuration: IssuerMap, 109 | } 110 | 111 | /** 112 | * Expected values for any open id token 113 | */ 114 | export interface IExpectedOpenIdToken extends IExpectedBase, IExpectedAudience { 115 | /** 116 | * Expected issuers configuration endpoint 117 | */ 118 | configuration: string, 119 | } 120 | -------------------------------------------------------------------------------- /lib/options/IValidationOptions.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import ClaimToken from '../verifiable_credential/ClaimToken'; 6 | import { IValidationResponse } from '../input_validation/IValidationResponse'; 7 | import { IPayloadProtectionSigning } from 'verifiablecredentials-crypto-sdk-typescript'; 8 | import { ValidationHelpers } from '../input_validation/ValidationHelpers'; 9 | import IValidatorOptions from './IValidatorOptions'; 10 | import { IExpectedBase, IExpectedVerifiablePresentation, IExpectedVerifiableCredential, IExpectedAudience } from './IExpected'; 11 | 12 | export type GetTokenObject = (validationResponse: IValidationResponse, token: string | object) => Promise; 13 | export type ResolveDidAndGetKeys = (validationResponse: IValidationResponse) => Promise; 14 | export type ValidateDidSignature = (validationResponse: IValidationResponse, token: IPayloadProtectionSigning) => Promise; 15 | export type CheckTimeValidityOnIdToken = (validationResponse: IValidationResponse, driftInSec?: number) => IValidationResponse; 16 | export type CheckTimeValidityOnToken = (validationResponse: IValidationResponse, driftInSec?: number) => IValidationResponse; 17 | export type CheckScopeValidityOnToken = (validationResponse: IValidationResponse, expected: IExpectedBase) => IValidationResponse; 18 | export type CheckScopeValidityOnIdToken = (validationResponse: IValidationResponse, expected: IExpectedAudience) => IValidationResponse; 19 | export type CheckScopeValidityOnVpToken = (validationResponse: IValidationResponse, expected: IExpectedVerifiablePresentation, siopDid: string) => IValidationResponse; 20 | export type CheckScopeValidityOnVcToken = (validationResponse: IValidationResponse, expected: IExpectedVerifiableCredential, siopDid: string) => IValidationResponse; 21 | export type FetchKeyAndValidateSignatureOnIdToken = (validationResponse: IValidationResponse, token: ClaimToken) => Promise; 22 | export type ValidateSignatureOnToken = (validationResponse: IValidationResponse, token: ClaimToken, key: any) => Promise; 23 | export type GetTokensFromSiop = (validationResponse: IValidationResponse) => IValidationResponse; 24 | export type FetchOpenIdTokenPublicKeys = (validationResponse: IValidationResponse, token: ClaimToken) => Promise; 25 | 26 | /** 27 | *Interface to model validation options 28 | */ 29 | export interface IValidationOptions { 30 | /** 31 | * The validator options 32 | */ 33 | validatorOptions: IValidatorOptions; 34 | 35 | /** 36 | * Gets the helpers 37 | */ 38 | validationHelpers: ValidationHelpers; 39 | 40 | /** 41 | * Get the token object from the request body 42 | */ 43 | getTokenObjectDelegate: GetTokenObject; 44 | 45 | /** 46 | * Resolve the DID and retrieve the public keys 47 | */ 48 | resolveDidAndGetKeysDelegate: ResolveDidAndGetKeys, 49 | 50 | /** 51 | * Validate DID signature 52 | */ 53 | validateDidSignatureDelegate: ValidateDidSignature, 54 | 55 | /** 56 | * Check the time validity of the token 57 | */ 58 | checkTimeValidityOnTokenDelegate: CheckTimeValidityOnToken, 59 | 60 | /** 61 | * Check the scope validity of the token 62 | */ 63 | checkScopeValidityOnSiopTokenDelegate: CheckScopeValidityOnToken, 64 | 65 | /** 66 | * Check the scope validity of the id token 67 | */ 68 | checkScopeValidityOnIdTokenDelegate: CheckScopeValidityOnIdToken, 69 | 70 | /** 71 | * Check the scope validity of the verifiable presentation token 72 | */ 73 | checkScopeValidityOnVpTokenDelegate: CheckScopeValidityOnVpToken, 74 | 75 | /** 76 | * Check the scope validity of the verifiable credential token 77 | */ 78 | checkScopeValidityOnVcTokenDelegate: CheckScopeValidityOnVcToken, 79 | 80 | /** 81 | * Delegate for getting a key and validate the signature on the token 82 | */ 83 | fetchKeyAndValidateSignatureOnIdTokenDelegate: FetchKeyAndValidateSignatureOnIdToken, 84 | 85 | /** 86 | * Signature validation 87 | */ 88 | validateSignatureOnTokenDelegate: ValidateSignatureOnToken, 89 | 90 | /** 91 | * fetch the public keys of an OpenId Provider 92 | */ 93 | fetchOpenIdTokenPublicKeysDelegate: FetchOpenIdTokenPublicKeys, 94 | } 95 | -------------------------------------------------------------------------------- /lib/options/IValidatorOptions.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { IDidResolver, Crypto, ValidationSafeguards } from '../index'; 7 | import IFetchRequest from '../tracing/IFetchRequest'; 8 | 9 | /** 10 | * Interface to model the validator options 11 | */ 12 | export default interface IValidatorOptions { 13 | 14 | /** 15 | * The DID resolver 16 | */ 17 | resolver: IDidResolver, 18 | 19 | /** 20 | * The fetch client 21 | */ 22 | fetchRequest: IFetchRequest, 23 | 24 | /** 25 | * The validation safeguards 26 | */ 27 | validationSafeguards: ValidationSafeguards, 28 | 29 | /** 30 | * Get the crypto options 31 | */ 32 | crypto: Crypto, 33 | 34 | /** 35 | * Gets the error value of an invalid token 36 | */ 37 | readonly invalidTokenError: number, 38 | 39 | /** 40 | * Flag indicating whether or not full siop validation must be performed 41 | */ 42 | readonly performFullSiopValidation: boolean, 43 | } 44 | -------------------------------------------------------------------------------- /lib/options/ValidationOptions.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import { FetchKeyAndValidateSignatureOnIdToken, IValidationOptions, CheckTimeValidityOnToken, ResolveDidAndGetKeys, GetTokenObject, ValidateDidSignature, CheckScopeValidityOnToken, ValidateSignatureOnToken, CheckScopeValidityOnVcToken, CheckScopeValidityOnIdToken, CheckScopeValidityOnVpToken, FetchOpenIdTokenPublicKeys } from './IValidationOptions'; 6 | import { ValidationHelpers } from '../input_validation/ValidationHelpers'; 7 | import IValidatorOptions from './IValidatorOptions'; 8 | import { TokenType } from '../index'; 9 | 10 | /** 11 | *Interface to model validation options 12 | */ 13 | export default class ValidationOptions implements IValidationOptions { 14 | 15 | /** 16 | * Create new instance of 17 | * @param validatorOptions The validator options 18 | * @param tokenType The type of token to validate 19 | */ 20 | constructor (public validatorOptions: IValidatorOptions, public tokenType: TokenType) { 21 | this.validationHelpers = new ValidationHelpers(validatorOptions, this, tokenType); 22 | this.getTokenObjectDelegate = this.validationHelpers.getTokenObject; 23 | 24 | this.resolveDidAndGetKeysDelegate = this.validationHelpers.resolveDidAndGetKeys; 25 | this.validateDidSignatureDelegate = this.validationHelpers.validateDidSignature; 26 | this.checkTimeValidityOnTokenDelegate = this.validationHelpers.checkTimeValidityOnToken; 27 | this.checkScopeValidityOnSiopTokenDelegate = this.validationHelpers.checkScopeValidityOnSiopToken; 28 | this.checkScopeValidityOnIdTokenDelegate = this.validationHelpers.checkScopeValidityOnIdToken; 29 | this.checkScopeValidityOnVpTokenDelegate = this.validationHelpers.checkScopeValidityOnVpToken; 30 | this.checkScopeValidityOnVcTokenDelegate = this.validationHelpers.checkScopeValidityOnVcToken; 31 | this.fetchKeyAndValidateSignatureOnIdTokenDelegate = this.validationHelpers.fetchKeyAndValidateSignatureOnIdToken; 32 | this.validateSignatureOnTokenDelegate = this.validationHelpers.validateSignatureOnToken; 33 | this.fetchOpenIdTokenPublicKeysDelegate = this.validationHelpers.fetchOpenIdTokenPublicKeys; 34 | } 35 | 36 | /** 37 | * Gets the helpers 38 | */ 39 | public validationHelpers: ValidationHelpers; 40 | 41 | /** 42 | * Get the token object from the request body 43 | */ 44 | public getTokenObjectDelegate: GetTokenObject; 45 | 46 | /** 47 | * Resolve the DID and get public keys 48 | */ 49 | public resolveDidAndGetKeysDelegate: ResolveDidAndGetKeys; 50 | 51 | /** 52 | * Validate the DID signatyre 53 | */ 54 | public validateDidSignatureDelegate: ValidateDidSignature; 55 | 56 | /** 57 | * Check the time validity of the token 58 | */ 59 | public checkTimeValidityOnTokenDelegate: CheckTimeValidityOnToken; 60 | 61 | /** 62 | * Check the scope validity of the token 63 | */ 64 | public checkScopeValidityOnSiopTokenDelegate: CheckScopeValidityOnToken; 65 | 66 | /** 67 | * Check the scope validity of the token 68 | */ 69 | public checkScopeValidityOnIdTokenDelegate: CheckScopeValidityOnIdToken; 70 | 71 | /** 72 | * Check the scope validity of the verifiable presentation token 73 | */ 74 | public checkScopeValidityOnVpTokenDelegate: CheckScopeValidityOnVpToken; 75 | 76 | /** 77 | * Check the scope validity of the verifiable credential token 78 | */ 79 | public checkScopeValidityOnVcTokenDelegate: CheckScopeValidityOnVcToken; 80 | 81 | /** 82 | * Delegate for getting a key and validate the signature on the token 83 | */ 84 | public fetchKeyAndValidateSignatureOnIdTokenDelegate: FetchKeyAndValidateSignatureOnIdToken; 85 | 86 | /** 87 | * Signature validation 88 | */ 89 | public validateSignatureOnTokenDelegate: ValidateSignatureOnToken; 90 | 91 | /** 92 | * fetch the public keys of an OpenId Provider 93 | */ 94 | public fetchOpenIdTokenPublicKeysDelegate: FetchOpenIdTokenPublicKeys; 95 | } 96 | -------------------------------------------------------------------------------- /lib/resolver/ManagedHttpResolver.ts: -------------------------------------------------------------------------------- 1 | import ErrorHelpers from '../error_handling/ErrorHelpers'; 2 | import { DidDocument, IDidResolver, IDidResolveResult, ValidationError } from '../index'; 3 | import FetchRequest from '../tracing/FetchRequest'; 4 | import IFetchRequest from '../tracing/IFetchRequest'; 5 | const errorCode = (error: number) => ErrorHelpers.errorCode('VCSDKMARE', error); 6 | 7 | require('es6-promise').polyfill(); 8 | require('isomorphic-fetch'); 9 | 10 | /** 11 | * Fetches DID Documents from remote resolvers over http and caching 12 | * the response for a specified period of time. 13 | * @class 14 | * @extends DidResolver 15 | */ 16 | export default class ManagedHttpResolver implements IDidResolver { 17 | /** 18 | * String to hold the formatted resolver url 19 | */ 20 | public readonly resolverUrl: string; 21 | 22 | /** 23 | * Create an instance of ManagedHttpResolver 24 | * @param universalResolverUrl the URL endpoint of the remote universal resolvers 25 | * @param fetchRequest optional fetch client 26 | */ 27 | constructor(universalResolverUrl: string, public fetchRequest?: IFetchRequest) { 28 | // Format and set the property for the 29 | const slash = universalResolverUrl.endsWith('/') ? '' : '/'; 30 | this.resolverUrl = `${universalResolverUrl}${slash}`; 31 | } 32 | 33 | /** 34 | * Looks up a DID Document 35 | * @inheritdoc 36 | */ 37 | public async resolve(did: string): Promise { 38 | const query = `${this.resolverUrl}${did}`; 39 | let response: Response; 40 | if (!this.fetchRequest) { 41 | this.fetchRequest = new FetchRequest(); 42 | } 43 | 44 | response = await this.fetchRequest.fetch(query, 'DIDResolve', { 45 | method: 'GET', 46 | headers: { 47 | } 48 | }); 49 | 50 | if (response.status >= 200 && response.status < 300) { 51 | const didDocument = await response.json(); 52 | return { 53 | didDocument: new DidDocument(didDocument.didDocument), 54 | metadata: didDocument.resolverMetadata 55 | } as IDidResolveResult; 56 | } 57 | 58 | return Promise.reject(new ValidationError(`Could not resolve ${query}`, errorCode(1))); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/revocation/IRevokedCard.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | /** 7 | * Interface to model revoced cards 8 | */ 9 | export default interface IRevocedCard { 10 | /** 11 | * The identifier of a Verifiable Credential, presented as the jti claim in a JWT 12 | */ 13 | verifiableCredentialId: string; 14 | 15 | /** 16 | * The epoch timestamp of when the action occurred 17 | */ 18 | actionTimeInSeconds: number; 19 | 20 | /** 21 | * Tenant Id of the Issuer of the Verifiable Credential 22 | */ 23 | tenantId: string; 24 | 25 | /** 26 | * The DID of the issuer performing the revocation 27 | */ 28 | issuerDid: string; 29 | } 30 | -------------------------------------------------------------------------------- /lib/rules_model/AuthenticationModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { RulesValidationError } from "../error_handling/RulesValidationError"; 7 | 8 | /** 9 | * Defines the presentation protcol 10 | */ 11 | export enum AuthenticationScheme { 12 | /** 13 | * basic authentication in the authorization header aka RFC 7617 14 | */ 15 | basic = "basic", 16 | 17 | /** 18 | * use a shared secret in a custom header 19 | */ 20 | sharedSecret = "sharedSecret", 21 | 22 | /** 23 | * use did authentication 24 | */ 25 | did = "did", 26 | 27 | /** 28 | * use did authentication with proof of possession 29 | */ 30 | didPop = "didPop", 31 | } 32 | 33 | /** 34 | * Data Model to describe external service authentication 35 | */ 36 | export class AuthenticationModel { 37 | 38 | /** 39 | * 40 | * @param type the type of shared secret 41 | * @param secret absolute url to the secret including the version 42 | * @param header the header used to transmit the secret when sharedSecret is being used 43 | */ 44 | constructor( 45 | public readonly type: AuthenticationScheme, 46 | public readonly secret: string, 47 | public readonly header?: string) 48 | { 49 | } 50 | 51 | /** 52 | * Populate an instance of AuthenticationModel from any instance 53 | * @param input object instance to populate from 54 | */ 55 | static fromJSON(input: any): AuthenticationModel { 56 | const { header, secret, type } = input; 57 | 58 | if (!secret) { 59 | throw new RulesValidationError('missing required "secret" property'); 60 | } 61 | 62 | if (!type) { 63 | throw new RulesValidationError('missing required "type" property'); 64 | } 65 | 66 | if (!Object.values(AuthenticationScheme).includes(type)) { 67 | throw new RulesValidationError(`${type} is not a valid AuthenticationScheme value`) 68 | } 69 | 70 | return new AuthenticationModel(type, secret, header); 71 | } 72 | } -------------------------------------------------------------------------------- /lib/rules_model/BaseAttestationModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { InputClaimModel } from './InputClaimModel'; 7 | 8 | /** 9 | * Base class for input attestations 10 | */ 11 | export abstract class BaseAttestationModel { 12 | 13 | /** 14 | * 15 | * @param mapping a map of string to InputClaimModel instances 16 | * @param encrypted flag indicating if the attestation is encrypted 17 | * @param claims an array of InputClaimModel values 18 | * @param required an array of InputClaimModel values 19 | * @param id the identifier of the attestation 20 | */ 21 | constructor( 22 | public mapping?: { [map: string]: InputClaimModel }, 23 | public encrypted = false, 24 | public claims?: InputClaimModel[], 25 | public required = false, 26 | private _id?: string, 27 | ) { } 28 | 29 | /** 30 | * Mapping keys of all index claims. 31 | */ 32 | public get indexClaims(): string[] { 33 | if (!this.mapping) { 34 | return []; 35 | } 36 | 37 | return Object.entries(this.mapping).filter(([_, { indexed }]) => indexed).map(([name]) => name); 38 | } 39 | 40 | /** 41 | * Gets the name of the attestation 42 | */ 43 | abstract get name(): string; 44 | 45 | /** 46 | * Gets the id of the attestation 47 | */ 48 | get id(): string { 49 | return this._id ?? this.name; 50 | } 51 | 52 | toJSON(): any { 53 | return { 54 | id: this.id, 55 | mapping: this.mapping, 56 | encrypted: this.encrypted, 57 | claims: this.claims, 58 | required: this.required, 59 | }; 60 | } 61 | 62 | /** 63 | * Populate an instance of BaseAttestationModel from any instance 64 | * @param input object instance to populate from 65 | */ 66 | populateFrom(input: any): void { 67 | this.encrypted = input.encrypted; 68 | this.claims = input.claims; 69 | this.mapping = {}; 70 | this.required = input.required; 71 | this._id = input.id; 72 | 73 | if (input.mapping) { 74 | for (let key of Object.keys(input.mapping)) { 75 | let claim = new InputClaimModel(); 76 | claim.populateFrom(input.mapping[key]); 77 | this.mapping[key] = claim; 78 | } 79 | } 80 | } 81 | 82 | /** 83 | * Create a IdTokenAttestationModel for an input resource 84 | */ 85 | forInput(): BaseAttestationModel { 86 | const arr: InputClaimModel[] = []; 87 | for (let key in this.mapping) { 88 | arr.push(this.mapping![key].forInput()); 89 | } 90 | 91 | return this.createForInput(arr); 92 | } 93 | 94 | /** 95 | * Creates Model given input claims. 96 | * @param claims Input claims 97 | */ 98 | protected abstract createForInput(claims: InputClaimModel[]): BaseAttestationModel; 99 | } 100 | -------------------------------------------------------------------------------- /lib/rules_model/BaseIssuanceModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { IssuanceAttestationsModel } from './IssuanceAttestationsModel'; 7 | 8 | /** 9 | * Base class for the modeling the Rules/Input files 10 | */ 11 | export abstract class BaseIssuanceModel { 12 | /** 13 | * 14 | * @param credentialIssuer url to the issuance endpoint of the Verifiable Credential 15 | * @param issuer the DID of the Verifiable Credential Issuer 16 | * @param attestations IssuanceAttestationsModel instance 17 | */ 18 | constructor (public credentialIssuer?: string, public issuer?: string, public attestations?: IssuanceAttestationsModel) {} 19 | 20 | /** 21 | * Populate an instance of BaseAttestationModel from any instance 22 | * @param input object instance to populate from 23 | */ 24 | populateFrom (input: any): void { 25 | if (input.attestations) { 26 | this.attestations = new IssuanceAttestationsModel(); 27 | this.attestations.populateFrom(input.attestations); 28 | } 29 | 30 | this.credentialIssuer = input.credentialIssuer; 31 | this.issuer = input.issuer; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/rules_model/DataProviderModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { RulesValidationError } from "../error_handling/RulesValidationError"; 7 | import { AuthenticationModel } from "./AuthenticationModel"; 8 | 9 | /** 10 | * Type that describes an object that maps a key to a value. 11 | */ 12 | export type DataProviderHeaders = { 13 | [header: string]: string; 14 | }; 15 | 16 | /** 17 | * Data Model to describe external service data providers 18 | */ 19 | export class DataProviderModel { 20 | /** 21 | * Default timeout for external calls 22 | */ 23 | public static readonly defaultTimeoutInMilliseconds = 500; 24 | 25 | /** 26 | * 27 | * @param id the id of the provider which must be a url 28 | * @param authentication the authentication scheme of the provider 29 | * @param headers headers to send in the request 30 | * @param timeoutInMilliseconds the timeout for the external call 31 | */ 32 | constructor( 33 | public readonly id: string, 34 | public readonly authentication: AuthenticationModel, 35 | public readonly headers: DataProviderHeaders = {}, 36 | public readonly timeoutInMilliseconds: number = DataProviderModel.defaultTimeoutInMilliseconds) { 37 | } 38 | 39 | /** 40 | * Populate an instance of DataProviderModel from any instance 41 | * @param input object instance to populate from 42 | * @param authentication AuthenticationModel instance from the parent object 43 | */ 44 | static fromJSON(input: any, authentication?: AuthenticationModel): DataProviderModel { 45 | const { id, authentication: inputAuthentication, headers = {}, timeoutInMilliseconds = DataProviderModel.defaultTimeoutInMilliseconds } = input; 46 | const authenticationInstance = inputAuthentication ? AuthenticationModel.fromJSON(inputAuthentication) : authentication; 47 | 48 | if (!id) { 49 | throw new RulesValidationError('missing required "id" property'); 50 | } 51 | 52 | if (!authenticationInstance) { 53 | // we must have a valid AuthenticationModel value set 54 | throw new RulesValidationError('authentication is not configured'); 55 | } 56 | 57 | return new DataProviderModel(id, authenticationInstance, headers, timeoutInMilliseconds); 58 | } 59 | } -------------------------------------------------------------------------------- /lib/rules_model/EventBindingModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { AuthenticationModel } from "./AuthenticationModel"; 7 | import { DataProviderModel } from "./DataProviderModel"; 8 | 9 | /** 10 | * Data Model to describe external service authentication 11 | */ 12 | export class EventBindingModel { 13 | 14 | /** 15 | * DataProviderModel instance describing token augmentation process 16 | */ 17 | public onTokenAugmentation?: DataProviderModel 18 | 19 | /** 20 | * Populate an instance of AuthenticationModel from any instance 21 | * @param input object instance to populate from 22 | * @param authentication AuthenticationModel instance from the parent object 23 | */ 24 | static fromJSON(input: any, authentication?: AuthenticationModel): EventBindingModel { 25 | const { onTokenAugmentation } = input; 26 | const result = new EventBindingModel(); 27 | 28 | if (onTokenAugmentation) { 29 | result.onTokenAugmentation = DataProviderModel.fromJSON(onTokenAugmentation, authentication); 30 | } 31 | 32 | return result; 33 | } 34 | } -------------------------------------------------------------------------------- /lib/rules_model/IdTokenAttestationModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { BaseAttestationModel } from './BaseAttestationModel'; 7 | import { InputClaimModel } from './InputClaimModel'; 8 | import { TrustedIssuerModel } from './TrustedIssuerModel'; 9 | 10 | /** 11 | * Model for defining Open Id Configuration for an Input contract 12 | */ 13 | export class IdTokenAttestationModel extends BaseAttestationModel { 14 | /** 15 | * 16 | * @param configuration url to an Open Id Connect Provider configuration 17 | * @param client_id if dynamic registration is not supported, the registered client to use for implicit authorization flows 18 | * @param redirect_uri if dynamic registration is not supported, the redirect_uri used for implicit authorization flows 19 | * @param scope scope value to augment the required openid value 20 | * @param mapping a map of string to InputClaimModel instances 21 | * @param encrypted flag indicating if the attestation is encrypted 22 | * @param claims an array of InputClaimModel values 23 | * @param required a flag indicating whether the attestation is required 24 | * @param id the identifier of the attestation 25 | * @param issuers an array of Trusted Issuers for the IdToken 26 | */ 27 | constructor( 28 | public configuration?: string, 29 | // tslint:disable-next-line:variable-name 30 | public client_id?: string, 31 | // tslint:disable-next-line:variable-name 32 | public redirect_uri?: string, 33 | public scope?: string, 34 | mapping?: { [map: string]: InputClaimModel }, 35 | encrypted: boolean = false, 36 | claims?: InputClaimModel[], 37 | required: boolean = false, 38 | id?: string, 39 | public issuers?: TrustedIssuerModel[], 40 | ) { 41 | super(mapping, encrypted, claims, required, id); 42 | } 43 | 44 | /** 45 | * Gets the name of the attestation 46 | */ 47 | get name(): string { 48 | return this.configuration!; 49 | } 50 | 51 | toJSON(): any { 52 | const result = super.toJSON(); 53 | result.configuration = this.configuration; 54 | result.client_id = this.client_id; 55 | result.issuers = this.issuers; 56 | result.redirect_uri = this.redirect_uri; 57 | result.scope = this.scope; 58 | return result; 59 | } 60 | 61 | /** 62 | * Populate an instance of IdTokenAttestationModel from any instance 63 | * @param input object instance to populate from 64 | */ 65 | populateFrom(input: any): void { 66 | super.populateFrom(input); 67 | this.configuration = input.configuration; 68 | this.client_id = input.client_id; 69 | this.redirect_uri = input.redirect_uri; 70 | this.scope = input.scope; 71 | 72 | if (input.issuers) { 73 | this.issuers = Array.from(input.issuers, (issuer) => { 74 | const t = new TrustedIssuerModel(); 75 | t.populateFrom(issuer); 76 | return t; 77 | }); 78 | } 79 | } 80 | 81 | /** 82 | * Creates Model given input claims. 83 | * @param claims Input claims 84 | */ 85 | protected createForInput(claims: InputClaimModel[]): BaseAttestationModel { 86 | return new IdTokenAttestationModel( 87 | this.configuration, 88 | this.client_id, 89 | this.redirect_uri, 90 | this.scope, 91 | undefined, 92 | this.encrypted, 93 | claims, 94 | this.required, 95 | undefined, 96 | this.issuers, 97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/rules_model/InputClaimModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { TransformModel } from './TransformModel'; 7 | 8 | /** 9 | * Model for representing an InputClaim in the Input file 10 | */ 11 | export class InputClaimModel { 12 | /** 13 | * 14 | * @param claim the name of the claim 15 | * @param type hint indicating what the type of the claim must be 16 | * @param required flag indicating if the claim is required 17 | * @param indexed flag indicating whether or not this claim may be indexed for Verifiable Credential searching 18 | * @param transform TransformModel instance 19 | */ 20 | constructor( 21 | public claim?: string, 22 | public type?: string, 23 | public required: boolean = false, 24 | public indexed: boolean = false, 25 | public transform?: TransformModel 26 | ) { 27 | } 28 | 29 | /** 30 | * Populate an instance of InputClaimModel from any instance 31 | * @param input object instance to populate from 32 | */ 33 | populateFrom(input: any): void { 34 | this.claim = input.claim; 35 | this.required = input.required; 36 | this.type = input.type; 37 | this.transform = input.transform; 38 | this.indexed = input.indexed; 39 | } 40 | 41 | /** 42 | * Creates an InputClaimInstance for a contract using a subset of properties 43 | */ 44 | forInput(): InputClaimModel { 45 | return new InputClaimModel(this.claim, this.type, this.required, this.indexed); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/rules_model/InputModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { BaseIssuanceModel } from './BaseIssuanceModel'; 7 | 8 | /** 9 | * Model for serializing Input 10 | */ 11 | export class InputModel extends BaseIssuanceModel { 12 | /** 13 | * Model id. 14 | */ 15 | public id: string = "input"; 16 | 17 | /** 18 | * 19 | * @param source IssuanceAttestationsModel instance to derive from 20 | */ 21 | constructor (source: BaseIssuanceModel) { 22 | super(source.credentialIssuer, source.issuer, source.attestations?.forInput()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/rules_model/RefreshConfigurationModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | /** 7 | * Model to express a Refresh Configuration 8 | */ 9 | export class RefreshConfigurationModel { 10 | /** 11 | * 12 | * @param validityInterval the interval in seconds for enabling refresh 13 | */ 14 | constructor (public validityInterval?: number) { 15 | } 16 | 17 | /** 18 | * Populate an instance of RemoteKeyAuthorizationModel from any instance 19 | * @param input object instance to populate from 20 | */ 21 | populateFrom (input: any): void { 22 | this.validityInterval = input.validityInterval; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/rules_model/RemoteKeyAuthorizationModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | /** 7 | * Model representing a Remote Key Authorization payload 8 | */ 9 | export class RemoteKeyAuthorizationModel { 10 | constructor (public method?: string) { 11 | } 12 | 13 | /** 14 | * Populate an instance of RemoteKeyAuthorizationModel from any instance 15 | * @param input object instance to populate from 16 | */ 17 | populateFrom (input: any): void { 18 | this.method = input.method; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/rules_model/RemoteKeyModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { AuthenticationModel } from './AuthenticationModel'; 7 | import { RemoteKeyAuthorizationModel } from './RemoteKeyAuthorizationModel'; 8 | 9 | /** 10 | * Model to express a Remote Key 11 | */ 12 | export class RemoteKeyModel { 13 | /** 14 | * 15 | * @param kid analog to the JOSE key id parameter which may be used to identify an arbitrary key 16 | * @param key url to a remote private key which may execute a decrypt/sign operation 17 | * @param x5t the thumbprint of a certificate identifying the public key counterpart of the private key 18 | * @param pfx url instructing the issuer about where to obtain a PKCS 12 to obtain the private key for a decrypt/sign operation 19 | * @param extractable flag indicating if the remote key is extractable 20 | * @param authorization deprecated 21 | * @param authentication an object that describes how the to authenticate to the remote signer 22 | */ 23 | constructor( 24 | public kid?: string, 25 | public key?: string, 26 | public x5t?: string, 27 | public pfx?: string, 28 | public extractable: boolean = false, 29 | public authorization?: RemoteKeyAuthorizationModel, 30 | public authentication?: AuthenticationModel, 31 | ) { } 32 | 33 | /** 34 | * Populate an instance of RemoteKeyAuthorizationModel from any instance 35 | * @param input object instance to populate from 36 | * @param authentication AuthenticationModel instance from the parent object 37 | */ 38 | populateFrom(input: any, authentication?: AuthenticationModel): void { 39 | this.kid = input.kid; 40 | this.key = input.key; 41 | this.x5t = input.x5t; 42 | this.pfx = input.pfx; 43 | this.extractable = input.extractable; 44 | 45 | if (input.authorization) { 46 | this.authorization = new RemoteKeyAuthorizationModel(); 47 | this.authorization.populateFrom(input.authorization); 48 | } 49 | 50 | // the root authentication instance may be overridden 51 | this.authentication = input.authentication ? AuthenticationModel.fromJSON(input.authentication) : authentication; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/rules_model/RulesPermissionModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { RulesValidationError } from '../error_handling/RulesValidationError'; 7 | 8 | export class RulesPermissionModel { 9 | /** 10 | * Creates a new RulesPermissionModel instance. 11 | * @param block Blocked DIDs. 12 | * @param allow Allowed DIDs. 13 | */ 14 | constructor (public block?: string[], public allow?: string[]) {} 15 | 16 | /** 17 | * Creates a new RulesPermissionModel instance from the given input. 18 | * @param input Input from which to populate instance. 19 | */ 20 | public static create(input: { [key: string]: any }): RulesPermissionModel { 21 | const instance = new RulesPermissionModel(); 22 | instance.populateFrom(input); 23 | return instance; 24 | } 25 | 26 | /** 27 | * Parses the given input into the current RulesPermissionModel instance. 28 | * @param input Input from which to populate instance. 29 | */ 30 | public populateFrom(input: { [key: string]: any }): void { 31 | const { allow, block } = input; 32 | this.allow = RulesPermissionModel.validateDidList(allow); 33 | this.block = RulesPermissionModel.validateDidList(block); 34 | 35 | if (!(this.allow || this.block)) { 36 | throw new RulesValidationError('Empty permissions models are not allowed.'); 37 | } 38 | } 39 | 40 | private static validateDidList (didList?: string[]): string[] | undefined { 41 | if (!didList) { 42 | return undefined; 43 | } 44 | 45 | const uniqueDids = new Set(didList); 46 | 47 | if (uniqueDids.size !== didList.length) { 48 | throw new RulesValidationError('Non-unique DID(s) found in one or more permission lists.'); 49 | } 50 | 51 | return didList; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/rules_model/SelfIssuedAttestationModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { InputClaimModel } from './InputClaimModel'; 7 | import { BaseAttestationModel } from './BaseAttestationModel'; 8 | 9 | /** 10 | * Model for defining Self Issued claims 11 | */ 12 | export class SelfIssuedAttestationModel extends BaseAttestationModel { 13 | 14 | public static readonly attestationName: string = 'selfAttested'; 15 | 16 | /** 17 | * 18 | * @param mapping a map of string to InputClaimModel instances 19 | * @param encrypted flag indicating if the attestation is encrypted 20 | * @param claims an array of InputClaimModel values 21 | * @param required a flag indicating whether the attestation is required 22 | * @param id the identifier of the attestation 23 | */ 24 | constructor(mapping?: { [map: string]: InputClaimModel }, encrypted: boolean = false, claims?: InputClaimModel[], required: boolean = false, id?: string) { 25 | super(mapping, encrypted, claims, required, id); 26 | } 27 | 28 | /** 29 | * Gets the name of the attestation 30 | */ 31 | get name(): string { 32 | return SelfIssuedAttestationModel.attestationName; 33 | } 34 | 35 | /** 36 | * Creates Model given input claims. 37 | * @param claims Input claims 38 | */ 39 | protected createForInput(claims: InputClaimModel[]): BaseAttestationModel { 40 | return new SelfIssuedAttestationModel(undefined, this.encrypted, claims, this.required); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/rules_model/TransformModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | /** 7 | * Model for representing a Transform property in an InputClaimModel 8 | */ 9 | export class TransformModel { 10 | /** 11 | * 12 | * @param name The name of the Transform function 13 | * @param remote A url to a remote transform function 14 | */ 15 | constructor (public name?: string, public remote?: string) { 16 | } 17 | 18 | /** 19 | * Populate an instance of TransformModel from any instance 20 | * @param input object instance to populate from 21 | */ 22 | populateFrom (input: any): void { 23 | this.name = input.name; 24 | this.remote = input.remote; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/rules_model/TrustedIssuerModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { RulesValidationError } from '../error_handling/RulesValidationError'; 7 | 8 | /** 9 | * Model for defining a Trusted Issuer for a Verifiable Credential 10 | */ 11 | export class TrustedIssuerModel { 12 | /** 13 | * Creates a Trusted Issuer instance 14 | * @param iss the Decentralized Identity of the Issuer 15 | */ 16 | constructor (public iss?: string) {} 17 | 18 | /** 19 | * Populate an instance of TrustedIssuerModel from any instance 20 | * @param input object instance to populate from 21 | */ 22 | populateFrom (input: any): void { 23 | const { iss } = input; 24 | 25 | if (!(iss && typeof iss === 'string')) { 26 | throw new RulesValidationError('Trusted issuer requires a valid DID for property \'iss\'.'); 27 | } 28 | 29 | this.iss = iss; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/rules_model/VerifiableCredentialModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | /** 7 | * Model for defining a Verifiable Credential 8 | */ 9 | export class VerifiableCredentialModel { 10 | /** 11 | * Verifiable credential contexts. 12 | */ 13 | '@context': string[]; 14 | 15 | constructor(context?: string[], public type?: string[], public credentialSubject?: any, public credentialStatus?: any, public credentialRefresh?: any) { 16 | if (context !== undefined) { 17 | this['@context'] = context; 18 | } 19 | } 20 | 21 | /** 22 | * Populate an instance of TrustedIssuerModel from any instance 23 | * @param input object instance to populate from 24 | */ 25 | populateFrom(input: any): void { 26 | if (input['@context'] !== undefined) { 27 | const arr = Array.from(input['@context']); 28 | this['@context'] = arr.map(c => c); 29 | } 30 | 31 | if (input.type !== undefined) { 32 | const arr = Array.from(input.type); 33 | this.type = arr.map(t => t); 34 | } 35 | 36 | this.credentialSubject = input.credentialSubject; 37 | this.credentialStatus = input.credentialStatus; 38 | this.credentialRefresh = input.credentialRefresh; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/rules_model/VerifiablePresentationAttestationModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { TrustedIssuerModel } from './TrustedIssuerModel'; 7 | import { BaseAttestationModel } from './BaseAttestationModel'; 8 | import { InputClaimModel } from './InputClaimModel'; 9 | 10 | /** 11 | * Represents a Verifiable Presentation in the input file 12 | */ 13 | export class VerifiablePresentationAttestationModel extends BaseAttestationModel { 14 | /** 15 | * Instantiates a VerifiablePresentationModel 16 | * @param credentialType the type of the Verifiable Credential as specified: https://www.w3.org/TR/vc-data-model/#types 17 | * @param validityInterval Expiry in seconds for the requested VP. 18 | * @param issuers an array of Trusted Issuers for the Verifiable Credential 19 | * @param endorsers an array of Trusted Endorsers for the Verifiable Credential 20 | * @param contracts an array of URLs to approved contracts which return the type of Verifiable Credential 21 | * @param mapping a map of string to InputClaimModel instances 22 | * @param encrypted flag indicating if the attestation is encrypted 23 | * @param claims an array of InputClaimModel values 24 | * @param required a flag indicating whether the attestation is required 25 | * @param id the identifier of the attestation 26 | */ 27 | constructor( 28 | public credentialType?: string, 29 | public validityInterval?: number, 30 | public issuers?: TrustedIssuerModel[], 31 | public endorsers?: TrustedIssuerModel[], 32 | public contracts?: string[], 33 | mapping?: { [map: string]: InputClaimModel }, 34 | encrypted: boolean = false, 35 | claims?: InputClaimModel[], 36 | required: boolean = false, 37 | id?: string) { 38 | super(mapping, encrypted, claims, required, id); 39 | } 40 | 41 | /** 42 | * Gets the name of the attestation 43 | */ 44 | get name(): string { 45 | return this.credentialType!; 46 | } 47 | 48 | toJSON(): any { 49 | const result = super.toJSON(); 50 | result.credentialType = this.credentialType; 51 | result.validityInterval = this.validityInterval; 52 | result.issuers = this.issuers; 53 | result.endorsers = this.endorsers; 54 | result.contracts = this.contracts; 55 | return result; 56 | } 57 | 58 | /** 59 | * Populate an instance of VerifiablePresentationModel from any instance 60 | * @param input object instance to populate from 61 | */ 62 | populateFrom(input: any): void { 63 | super.populateFrom(input); 64 | this.credentialType = input.credentialType; 65 | this.validityInterval = input.validityInterval; 66 | this.contracts = input.contracts; 67 | 68 | if (input.issuers) { 69 | const arr = Array.from(input.issuers); 70 | this.issuers = arr.map(VerifiablePresentationAttestationModel.createTrustedIssuer); 71 | } 72 | 73 | if (input.endorsers) { 74 | const arr = Array.from(input.endorsers); 75 | this.endorsers = arr.map(VerifiablePresentationAttestationModel.createTrustedIssuer); 76 | } 77 | } 78 | 79 | private static createTrustedIssuer(issuer: any): TrustedIssuerModel { 80 | const t = new TrustedIssuerModel(); 81 | t.populateFrom(issuer); 82 | return t; 83 | } 84 | 85 | /** 86 | * Creates Model given input claims. 87 | * @param claims Input claims 88 | */ 89 | protected createForInput(claims: InputClaimModel[]): BaseAttestationModel { 90 | return new VerifiablePresentationAttestationModel( 91 | this.credentialType, 92 | this.validityInterval, 93 | this.issuers, 94 | this.endorsers, 95 | this.contracts, 96 | undefined, 97 | this.encrypted, 98 | claims, 99 | this.required 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /lib/rules_model/presentation_exchange/PresentationDefinitionModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { PresentationExchangeInputDescriptorModel } from '../../index'; 7 | 8 | /** 9 | * Class to model the presentation_definition as defined by Presentation Exchange. 10 | * See https://identity.foundation/presentation-exchange/#presentation-definition 11 | */ 12 | export class PresentationDefinitionModel { 13 | /** 14 | * Create an instance of PresentationDefinitionModel 15 | * @param PresentationExchangeInputDescriptorModel PresentationExchangeInputDescriptorModel instance 16 | */ 17 | constructor ( 18 | /** 19 | * The resource MUST contain this property, and its value MUST be an array of Input Descriptor objects. 20 | */ 21 | public input_descriptors?: PresentationExchangeInputDescriptorModel[], 22 | 23 | /** 24 | * The resource MAY contain this property, and if present its value SHOULD be a human-friendly name that describes what the Presentation Definition pertains to. 25 | */ 26 | public name?: string, 27 | /** 28 | * The resource MAY contain this property, and if present its value MUST be a string that describes the purpose for which the Presentation Definition’s inputs are being requested. 29 | */ 30 | public purpose?: string) { 31 | } 32 | 33 | /** 34 | * Populate this object from a model 35 | * @param input model to populate object 36 | */ 37 | public populateFrom(input: PresentationDefinitionModel): PresentationDefinitionModel { 38 | this.name = input.name; 39 | this.purpose = input.purpose; 40 | this.input_descriptors = []; 41 | const objectToPopulate = new PresentationExchangeInputDescriptorModel(); 42 | for (let inx = 0; input.input_descriptors && inx < input.input_descriptors.length ; inx++ ) { 43 | const item: PresentationExchangeInputDescriptorModel = input.input_descriptors[inx]; 44 | this.input_descriptors.push(objectToPopulate.populateFrom(item)); 45 | } 46 | return this; 47 | } 48 | } -------------------------------------------------------------------------------- /lib/rules_model/presentation_exchange/PresentationExchangeConstraintsModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { BaseIssuanceModel } from '../../index'; 7 | 8 | /** 9 | * Class to model the constraints as defined by Presentation Exchange. 10 | */ 11 | export class PresentationExchangeConstraintsModel { 12 | /** 13 | * Create an instance of PresentationExchangeConstraintsModel 14 | * @param path for the constraint 15 | * @param name for the schema 16 | * @param purpose of the schema 17 | */ 18 | constructor ( 19 | 20 | /** 21 | * The object MUST contain a path property, and its value MUST be an array of one or more JSONPath string expressions, as defined in the JSONPath Syntax Definition section, that select some subset of values from the target input. 22 | */ 23 | public path?: string[], 24 | 25 | /** 26 | * The object MAY contain a filter property, and if present its value MUST be JSON Schema descriptor used to filter against the values returned from evaluation of the JSONPath string expressions in the path array. 27 | */ 28 | public filter?: any, 29 | /** 30 | * The object MAY contain a purpose property, and if present its value MUST be a string that describes the purpose for which the field is being requested. 31 | */ 32 | public purpose?: string) { 33 | } 34 | 35 | /** 36 | * Populate this object from a model 37 | * @param input model to populate object 38 | */ 39 | public populateFrom(_input: PresentationExchangeConstraintsModel): PresentationExchangeConstraintsModel { 40 | return this; 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /lib/rules_model/presentation_exchange/PresentationExchangeInputDescriptorModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { PresentationExchangeSchemaModel, PresentationExchangeIssuanceModel, PresentationExchangeConstraintsModel } from '../../index'; 7 | 8 | /** 9 | * Class to model the preseninput_descriptors as defined by Presentation Exchange. 10 | * Input Descriptors are objects that describe what type of input data/credential, or sub-fields thereof, is required for submission to the Verifier. 11 | */ 12 | export class PresentationExchangeInputDescriptorModel { 13 | /** 14 | * Create an instance of PresentationExchangeInputDescriptorModel 15 | * @param id for the input definition 16 | * @param schema schema for the input definition 17 | * @param issuance issuance for the input definition 18 | * @param constraints constraints for the input definition 19 | */ 20 | constructor( 21 | public id?: string, 22 | public schema?: PresentationExchangeSchemaModel, 23 | public issuance?: PresentationExchangeIssuanceModel[], 24 | public constraints?: PresentationExchangeConstraintsModel) { 25 | } 26 | 27 | /** 28 | * Populate this object from a model 29 | * @param input model to populate object 30 | */ 31 | public populateFrom(input: PresentationExchangeInputDescriptorModel): PresentationExchangeInputDescriptorModel { 32 | this.id = input.id; 33 | if (input.schema) { 34 | const objectToPopulate = new PresentationExchangeSchemaModel(); 35 | this.schema = objectToPopulate.populateFrom(input.schema); 36 | } 37 | if (input.issuance) { 38 | this.issuance = []; 39 | const objectToPopulate = new PresentationExchangeIssuanceModel(); 40 | for (let inx = 0; inx < input.issuance.length; inx++) { 41 | const item: PresentationExchangeIssuanceModel = input.issuance[inx]; 42 | this.issuance.push(objectToPopulate.populateFrom(item)); 43 | } 44 | } else { 45 | delete this.issuance; 46 | } 47 | 48 | return this; 49 | } 50 | } -------------------------------------------------------------------------------- /lib/rules_model/presentation_exchange/PresentationExchangeIssuanceModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { BaseIssuanceModel } from '../../index'; 7 | 8 | /** 9 | * Class to model the issuance as defined by Presentation Exchange. 10 | */ 11 | export class PresentationExchangeIssuanceModel { 12 | /** 13 | * Create an instance of PresentationExchangeIssuanceModel 14 | * @param manifest for the issuance (contract) 15 | */ 16 | constructor ( 17 | 18 | /** 19 | * The url to the contract 20 | */ 21 | public manifest?: string) { 22 | } 23 | 24 | /** 25 | * Populate this object from a model 26 | * @param input model to populate object 27 | */ 28 | public populateFrom(input: PresentationExchangeIssuanceModel): PresentationExchangeIssuanceModel { 29 | this.manifest = input.manifest; 30 | return this; 31 | } 32 | } -------------------------------------------------------------------------------- /lib/rules_model/presentation_exchange/PresentationExchangeSchemaModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { BaseIssuanceModel } from '../../index'; 7 | 8 | /** 9 | * Class to model the schema as defined by Presentation Exchange. 10 | */ 11 | export class PresentationExchangeSchemaModel { 12 | /** 13 | * Create an instance of PresentationExchangeSchemaModel 14 | * @param url for the schema 15 | * @param name for the schema 16 | * @param purpose of the schema 17 | */ 18 | constructor ( 19 | 20 | /** 21 | * Unique identifier for the requested type 22 | */ 23 | public uri?: string[], 24 | 25 | /** 26 | * The resource MAY contain this property, and if present its value SHOULD be a human-friendly name that describes what the Presentation Definition pertains to. 27 | */ 28 | public name?: string, 29 | /** 30 | * The resource MAY contain this property, and if present its value MUST be a string that describes the purpose for which the Presentation Definition’s inputs are being requested. 31 | */ 32 | public purpose?: string) { 33 | } 34 | 35 | /** 36 | * Populate this object from a model 37 | * @param input model to populate object 38 | */ 39 | public populateFrom(input: PresentationExchangeSchemaModel): PresentationExchangeSchemaModel { 40 | this.name = input.name; 41 | this.purpose = input.purpose; 42 | this.uri = []; 43 | for (let inx = 0; input.uri && inx < input.uri.length ; inx++ ) { 44 | this.uri.push(input.uri[inx]); 45 | } 46 | 47 | return this; 48 | } 49 | } -------------------------------------------------------------------------------- /lib/tracing/FetchRequest.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import AbortController from 'abort-controller'; 7 | import { Agent } from 'https'; 8 | import IFetchRequest from "./IFetchRequest"; 9 | 10 | /** 11 | * Do the fetch requests. 12 | * It allows a service to implement its own fetch with instrumentation. 13 | */ 14 | export default class FetchRequest implements IFetchRequest { 15 | 16 | /** 17 | * Agent instance for connection reuse 18 | */ 19 | private readonly agent: Agent; 20 | 21 | /** 22 | * Create new instance of FetchRequest 23 | */ 24 | constructor() { 25 | this.agent = new Agent({ keepAlive: true }); 26 | } 27 | 28 | /** 29 | * Do fetch call 30 | * @param url to fetch 31 | * @param callingMethodName Calling method name 32 | * @param options fetch options 33 | */ 34 | public async fetch(url: string, _callingMethodName: string, options: any): Promise { 35 | if (options) { 36 | options.agent = this.agent 37 | } else { 38 | options = { 39 | agent: this.agent 40 | } 41 | } 42 | 43 | // Get timeout from options. Default to 10 seconds. 44 | const { timeout = 10000 } = options; 45 | const abortController = new AbortController(); 46 | options.signal = abortController.signal; 47 | 48 | const id = setTimeout(() => { 49 | abortController.abort() 50 | }, timeout); 51 | 52 | const response = await fetch(url, options); 53 | clearTimeout(id); 54 | return response; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/tracing/IFetchRequest.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | /** 7 | * Interface to do fetch requests. 8 | * It allows a service to implement its own fetch with instrumentation. 9 | */ 10 | export default interface IFetchRequest { 11 | 12 | /** 13 | * Do fetch call 14 | * @param url to fetch 15 | * @param callingMethodName Calling method name 16 | * @param options fetch options 17 | */ 18 | fetch(url: string, callingMethodName: string, options?: any): Promise; 19 | } 20 | -------------------------------------------------------------------------------- /lib/verifiable_credential/ClaimBag.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import VerifiableCredentialConstants from './VerifiableCredentialConstants'; 6 | 7 | /** 8 | * Model for the claim bag used by the verifiable credential issuance 9 | */ 10 | export default class ClaimBag { 11 | private _vcTypes: string[] = [VerifiableCredentialConstants.DEFAULT_VERIFIABLECREDENTIAL_TYPE]; 12 | private _context: string[] = [VerifiableCredentialConstants.DEFAULT_VERIFIABLECREDENTIAL_CONTEXT]; 13 | private _credentialStatus: any = undefined; 14 | 15 | /** 16 | * Claim used in CredentialSubject 17 | */ 18 | public claims: { [prop: string]: any } = {}; 19 | /** 20 | * Gets the type property 21 | */ 22 | public get types(): string[] { 23 | return this._vcTypes; 24 | } 25 | 26 | /** 27 | * Gets the context property 28 | */ 29 | public get context(): string[] { 30 | return this._context; 31 | } 32 | 33 | /** 34 | * Gets the credentialStatus property 35 | */ 36 | public get credentialStatus(): any { 37 | return this._credentialStatus; 38 | } 39 | 40 | /** 41 | * sets the credentialStatus property 42 | */ 43 | public set credentialStatus(value: any) { 44 | this._credentialStatus = value; 45 | } 46 | 47 | /** 48 | * Add or update a claim in the claim bag 49 | * @param type Claim type 50 | * @param values Claim value 51 | */ 52 | public addOrUpdate(type: string, values: any) { 53 | this.claims[type] = values; 54 | } 55 | } -------------------------------------------------------------------------------- /lib/verifiable_credential/JsonWebSignatureToken.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import base64url from 'base64url'; 6 | import ErrorHelpers from '../error_handling/ErrorHelpers'; 7 | import ValidationError from '../error_handling/ValidationError'; 8 | const errorCode = (error: number) => ErrorHelpers.errorCode('VCSDKJWST', error); 9 | 10 | /** 11 | * Type that describes an object that maps a key to a value. 12 | */ 13 | export type TokenPayload = { [claim: string]: any }; 14 | 15 | /** 16 | * class for parsing unecrypted Json Web Tokens 17 | */ 18 | export default class JsonWebSignatureToken { 19 | /** 20 | * The expected number of parts for a JWS compact token 21 | */ 22 | private static readonly EXPECTED_PARTS: number = 3; 23 | 24 | /** 25 | * Encoding of the header as mandated per JWS rfc: https://tools.ietf.org/html/rfc7515#section-2 26 | */ 27 | private static readonly UTF8_ENCODING = 'utf8'; 28 | 29 | /** 30 | * flag indicating whether or not the token is an unsecured jws 31 | */ 32 | public readonly unsecured: boolean; 33 | 34 | /** 35 | * Decoded header of the token 36 | */ 37 | public readonly header: TokenPayload; 38 | 39 | /** 40 | * decoded payload of the token 41 | */ 42 | public readonly payload: TokenPayload; 43 | 44 | /** 45 | * encoded signature of the token 46 | */ 47 | public readonly signature: string; 48 | 49 | /** 50 | * the length of the pre-image of the token 51 | * per RFC7515: https://tools.ietf.org/html/rfc7515#section-3.3 52 | */ 53 | private readonly preimageLength: number; 54 | 55 | /** 56 | * Create a parsed JsonWebSignatureToken per https://tools.ietf.org/html/rfc7515#section-7.1 57 | * @param token jws compact token string 58 | */ 59 | constructor(public readonly token: string) { 60 | const parts = token.split('.'); 61 | 62 | // a signed jws is header.payload.signature per https://tools.ietf.org/html/rfc7515#section-7.1 63 | // a unsecured jws is header.payload. per https://tools.ietf.org/html/rfc7515#appendix-A.5 64 | if (parts.length < JsonWebSignatureToken.EXPECTED_PARTS) { 65 | throw new ValidationError('Invalid json web token', errorCode(1)); 66 | } 67 | 68 | this.header = JsonWebSignatureToken.parsePayload(parts[0], 'header', 2); 69 | this.payload = JsonWebSignatureToken.parsePayload(parts[1], 'payload', 3); 70 | this.signature = parts[2]; 71 | this.unsecured = !parts[2]; 72 | 73 | // the preimage does not include a the trailing seperator 74 | this.preimageLength = 1 + parts[0].length + parts[1].length; 75 | } 76 | 77 | /** 78 | * Encode a protected header and payload into a unsecured compact jws 79 | * @param header TokenPayload instance 80 | * @param payload TokenPayload instance 81 | * @returns JsonWebSignatureToken instance 82 | */ 83 | public static encode(header: TokenPayload, payload: TokenPayload): JsonWebSignatureToken { 84 | const encodedHeader = base64url.encode(JSON.stringify(header), JsonWebSignatureToken.UTF8_ENCODING); 85 | const encodedPayload = base64url.encode(JSON.stringify(payload), JsonWebSignatureToken.UTF8_ENCODING); 86 | return new JsonWebSignatureToken(`${encodedHeader}.${encodedPayload}.`); 87 | } 88 | 89 | /** 90 | * parse a payload 91 | * @param encodedPart encoded string 92 | * @param name name of the part 93 | * @param scenario scenario id 94 | * @returns TokenPayload instance 95 | */ 96 | private static parsePayload(encodedPart: string, name: string, scenario: number): TokenPayload { 97 | try { 98 | return JSON.parse(base64url.decode(encodedPart)); 99 | } catch (error) { 100 | throw new ValidationError(`Invalid json web token the ${name} is malformed`, errorCode(scenario)); 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /lib/verifiable_credential/VerifiableCredentialConstants.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | /** 7 | * Class for verifyable credentials constants 8 | */ 9 | export default class VerifiableCredentialConstants { 10 | /** 11 | * Constant for claims 12 | */ 13 | public static ATTESTATIONS = 'attestations'; 14 | 15 | /** 16 | * Constant for presentation exchange claims 17 | */ 18 | public static PRESENTATION_SUBMISSION = 'presentation_submission'; 19 | 20 | /** 21 | * Constant for status request receipts 22 | */ 23 | public static RECEIPT = 'receipt'; 24 | 25 | /** 26 | * Constant for context 27 | */ 28 | public static CLAIM_CONTEXT = '@context'; 29 | 30 | /** 31 | * Constant for default context 32 | */ 33 | public static DEFAULT_VERIFIABLECREDENTIAL_CONTEXT = 'https://www.w3.org/2018/credentials/v1'; 34 | 35 | /** 36 | * Constant for default type 37 | */ 38 | public static DEFAULT_VERIFIABLECREDENTIAL_TYPE = 'VerifiableCredential'; 39 | 40 | /** 41 | * Constant for default type 42 | */ 43 | public static DEFAULT_VERIFIABLEPRESENTATION_TYPE = 'VerifiablePresentation'; 44 | 45 | /** 46 | * Constant for self issued claims 47 | */ 48 | public static CLAIMS_SELFISSUED = 'selfIssued'; 49 | 50 | /** 51 | * Constant for id tokens claims 52 | */ 53 | public static CLAIMS_IDTOKENS = 'idTokens'; 54 | 55 | /** 56 | * Constant found in names of verifiable credentials 57 | */ 58 | public static CLAIMS_VERIFIABLECREDENTIAL = '#vc.'; 59 | 60 | /** 61 | * Constant found in names of verifiable credentials 62 | */ 63 | public static CLAIMS_VERIFIABLECREDENTIAL_IN_SOURCES = 'vp'; 64 | 65 | /** 66 | * Constant for id tokens claims 67 | */ 68 | public static VC_JTI_PREFIX = 'urn:pic:'; 69 | 70 | /** 71 | * Constant for id tokens claims 72 | */ 73 | public static CONFIG_JWKS = 'jwks_uri'; 74 | 75 | /** 76 | * Constant for kid claim 77 | */ 78 | public static TOKEN_KID = 'kid'; 79 | 80 | /** 81 | * Constant for self issued issuer 82 | */ 83 | public static TOKEN_SI_ISS = 'https://self-issued.me'; 84 | 85 | /** 86 | * Default resolver url 87 | */ 88 | public static UNIVERSAL_RESOLVER_URL = 'https://beta.discover.did.msidentity.com/1.0/identifiers/'; 89 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verifiablecredentials-verification-sdk-typescript", 3 | "version": "0.12.1-preview.60", 4 | "description": "Typescript SDK for verifiable credentials", 5 | "main": "dist/lib/index.js", 6 | "types": "dist/lib/index.d.ts", 7 | "scripts": { 8 | "build": "tsc --p tsconfig.json", 9 | "test": "nyc jasmine-ts --config=./tests/jasmine.json", 10 | "lint": "tslint --fix --project . --config node_modules/ad-did-services-common/tslint.json" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/microsoft/VerifiableCredentials-Verification-SDK-Typescript" 15 | }, 16 | "keywords": [], 17 | "author": "", 18 | "license": "MIT", 19 | "devDependencies": { 20 | "@types/jasmine": "3.6.3", 21 | "@types/node": "14.6.2", 22 | "@types/node-fetch": "2.5.5", 23 | "@types/uuid": "3.4.4", 24 | "delay": "^4.4.0", 25 | "fetch-mock": "9.3.1", 26 | "jasmine": "^3.6.3", 27 | "jasmine-reporters": "^2.3.2", 28 | "jasmine-spec-reporter": "^4.2.1", 29 | "jasmine-ts": "0.4.0", 30 | "node-fetch": "^2.6.1", 31 | "nyc": "^15.1.0", 32 | "source-map-support": "0.5.12", 33 | "ts-node": "8.1.0", 34 | "tslint": "5.16.0", 35 | "tslint-config-standard": "8.0.1", 36 | "typescript": "4.1.2", 37 | "typescript-map": "0.0.7" 38 | }, 39 | "dependencies": { 40 | "@azure/identity": "1.0.0", 41 | "@decentralized-identity/did-common-typescript": "0.1.19", 42 | "abort-controller": "3.0.0", 43 | "base64url": "3.0.1", 44 | "bs58": "4.0.1", 45 | "clone": "2.1.2", 46 | "deep-property-access": "1.0.1", 47 | "es6-promise": "^4.2.8", 48 | "isomorphic-fetch": "3.0.0", 49 | "jsonpath": "1.1.1", 50 | "multihashes": "0.4.14", 51 | "uuid": "7.0.1", 52 | "verifiablecredentials-crypto-sdk-typescript": "1.1.12-preview.12" 53 | }, 54 | "nyc": { 55 | "extension": [ 56 | ".ts", 57 | ".tsx" 58 | ], 59 | "include": [ 60 | "lib/**" 61 | ], 62 | "reporter": [ 63 | "text", 64 | "cobertura", 65 | "html" 66 | ] 67 | }, 68 | "files": [ 69 | "dist/**", 70 | "lib/**", 71 | "README.md", 72 | "LICENSE", 73 | "package.json" 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /runtest.bat: -------------------------------------------------------------------------------- 1 | call npm run build 2 | node .\node_modules\jasmine-ts --config=./tests/jasmine.json -------------------------------------------------------------------------------- /tests/Credentials.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your Key Vault credentials 3 | */ 4 | 5 | export default class Credentials { 6 | public static tenantGuid = 'Your tenant GUID here'; 7 | public static clientId = 'Your key vault client id here'; 8 | public static clientSecret = 'Your key vault client secret here'; 9 | public static vaultUri = 'Your key vault uri here' 10 | } 11 | 12 | -------------------------------------------------------------------------------- /tests/ErrorHelpers.spec.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import ErrorHelpers from "../lib/error_handling/ErrorHelpers"; 7 | 8 | describe('ErrorHelpers', () => { 9 | it('should return the correct error code', () => { 10 | expect(ErrorHelpers.errorCode('ABC', 5)).toEqual('ABC05'); 11 | expect(ErrorHelpers.errorCode('ABC', 15)).toEqual('ABC15'); 12 | expect(ErrorHelpers.errorCode('ABC', 15, 1)).toEqual('ABC15'); 13 | expect(ErrorHelpers.errorCode('ABC', 15, 4)).toEqual('ABC0015'); 14 | }); 15 | }); -------------------------------------------------------------------------------- /tests/FetchRequest.spec.ts: -------------------------------------------------------------------------------- 1 | import FetchRequest from "../lib/tracing/FetchRequest"; 2 | const fetchMock = require('fetch-mock'); 3 | const delay = require('delay'); 4 | 5 | describe('FetchRequest', () => { 6 | beforeEach(() => { fetchMock.reset() }); 7 | 8 | it('should do fetch', async () => { 9 | fetchMock.get('https://example', { prop1: 'prop1' }); 10 | 11 | let fetchRequest = new FetchRequest(); 12 | let response = await fetchRequest.fetch('https://example', 'testing', { method: 'GET' }); 13 | expect(response.ok).toBeTruthy(); 14 | 15 | let body = await response.json(); 16 | expect(body['prop1']).toEqual('prop1'); 17 | 18 | // check fetch without options 19 | response = await fetchRequest.fetch('https://example', 'testing', undefined); 20 | expect(response.ok).toBeTruthy(); 21 | }); 22 | 23 | it('should timeout the fetch', async () => { 24 | fetchMock.get('https://example', async () => { 25 | await delay(500); 26 | return { prop1: 'prop1' } 27 | }); 28 | 29 | let fetchRequest = new FetchRequest(); 30 | try { 31 | await fetchRequest.fetch('https://example', 'testing', { 32 | method: 'GET', 33 | timeout: 10 34 | }); 35 | fail('Timeout did not occur'); 36 | } catch (exception) { 37 | expect(exception.message).toEqual('The operation was aborted.'); 38 | } 39 | }); 40 | }); -------------------------------------------------------------------------------- /tests/IdTokenAttestationModel.spec.ts: -------------------------------------------------------------------------------- 1 | import { IdTokenAttestationModel, RulesValidationError } from '../lib'; 2 | 3 | describe('IdTokenAttestationModel', () => { 4 | let idTokenAttestationModelInput: any; 5 | 6 | beforeAll(() => { 7 | idTokenAttestationModelInput = { 8 | configuration: 'https://example.com/some-oidc-endpoint', 9 | client_id: 'client_id', 10 | redirect_uri: 'redirect_uri', 11 | mapping: { claimMapping1: { type: 'String', claim: 'claim1' } }, 12 | }; 13 | }); 14 | 15 | describe('populateFrom()', () => { 16 | it('should fail with undefined trusted issuer DID', () => { 17 | const undefinedDidIssuers = [{ iss: 'did:ion:issuer1' }, {}]; 18 | const idTokenAttestationModel = new IdTokenAttestationModel(); 19 | 20 | try { 21 | idTokenAttestationModel.populateFrom({ ...idTokenAttestationModelInput, issuers: undefinedDidIssuers }); 22 | fail('No error was thrown.'); 23 | } catch (error) { 24 | expect(error instanceof RulesValidationError).toEqual(true); 25 | } 26 | }); 27 | 28 | it('should fail with invalid trusted issuer DID', () => { 29 | const trivialDidIssuers = [{ iss: 'did:ion:issuer1' }, { iss: '' }]; 30 | const idTokenAttestationModel = new IdTokenAttestationModel(); 31 | 32 | try { 33 | idTokenAttestationModel.populateFrom({ ...idTokenAttestationModelInput, issuers: trivialDidIssuers }); 34 | fail('No error was thrown.'); 35 | } catch (error) { 36 | expect(error instanceof RulesValidationError).toEqual(true); 37 | } 38 | }); 39 | 40 | it('should fail with non-string trusted issuer DID', () => { 41 | const nonStringDidIssuers = [{ iss: 1 }]; 42 | const idTokenAttestationModel = new IdTokenAttestationModel(); 43 | 44 | try { 45 | idTokenAttestationModel.populateFrom({ ...idTokenAttestationModelInput, issuers: nonStringDidIssuers }); 46 | fail('No error was thrown.'); 47 | } catch (error) { 48 | expect(error instanceof RulesValidationError).toEqual(true); 49 | } 50 | }); 51 | }); 52 | }); -------------------------------------------------------------------------------- /tests/IdTokenValidation.spec.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import TestSetup from './TestSetup'; 6 | import { IdTokenValidation } from '../lib/input_validation/IdTokenValidation'; 7 | import { IssuanceHelpers } from './IssuanceHelpers'; 8 | import { ClaimToken, IExpectedIdToken, TokenType, Validator, ValidatorBuilder } from '../lib'; 9 | 10 | describe('idTokenValidation', () => { 11 | let setup: TestSetup; 12 | const originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; 13 | 14 | beforeAll(() => { 15 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; 16 | }); 17 | 18 | beforeEach(() => { 19 | setup = new TestSetup(); 20 | }); 21 | 22 | afterEach(() => { 23 | setup.fetchMock.reset(); 24 | }); 25 | 26 | afterAll(() => { 27 | jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; 28 | }); 29 | 30 | it('should test validate', async () => { 31 | const [_, options, siop] = await IssuanceHelpers.createRequest(setup, TokenType.idToken, true); 32 | const expected = siop.expected.filter((token: IExpectedIdToken) => token.type === TokenType.idToken)[0]; 33 | const { contract, idToken } = siop; 34 | 35 | let validator = new IdTokenValidation(options, expected, Validator.readContractId(contract)); 36 | let response = await validator.validate(idToken); 37 | expect(response.result).toBeTruthy(); 38 | 39 | const altExpected: any = {configuration: { schema: ['http://example/configuration'] } } 40 | validator = new IdTokenValidation(options, altExpected, Validator.readContractId(contract)); 41 | response = await validator.validate(idToken); 42 | expect(response.result).toBeTruthy(); 43 | 44 | // Negative cases 45 | // Bad id token signature 46 | validator = new IdTokenValidation(options, expected, Validator.readContractId(contract)); 47 | response = await validator.validate(new ClaimToken(TokenType.idToken, idToken.rawToken + 'a', idToken.id)); 48 | expect(response.result).toBeFalsy(); 49 | expect(response.status).toEqual(ValidatorBuilder.INVALID_TOKEN_STATUS_CODE); 50 | expect(response.detailedError).toEqual('The presented idToken is has an invalid signature'); 51 | expect(response.code).toEqual('VCSDKVaHe37'); 52 | 53 | // missing siopcontract 54 | validator = new IdTokenValidation(options, {configuration: {} }, undefined); 55 | response = await validator.validate(idToken); 56 | expect(response.result).toBeFalsy(response.detailedError); 57 | expect(response.detailedError).toEqual('The siopContract needs to be specified to validate the idTokens.'); 58 | expect(response.code).toEqual('VCSDKIDVa03'); 59 | 60 | // missing configuration array in expected 61 | validator = new IdTokenValidation(options, {configuration: {} }, Validator.readContractId(contract)); 62 | response = await validator.validate(idToken); 63 | expect(response.result).toBeFalsy(response.detailedError); 64 | expect(response.detailedError).toEqual(`Expected should have configuration issuers set for idToken. Missing configuration for 'schema'.`); 65 | expect(response.code).toEqual('VCSDKIDVa04'); 66 | 67 | // empty array in configuration 68 | validator = new IdTokenValidation(options, {configuration: [] }, Validator.readContractId(contract)); 69 | response = await validator.validate(idToken); 70 | expect(response.result).toBeFalsy(response.detailedError); 71 | expect(response.detailedError).toEqual('Expected should have configuration issuers set for idToken. Empty array presented.'); 72 | expect(response.code).toEqual('VCSDKIDVa02'); 73 | 74 | // missing configuration in expected 75 | validator = new IdTokenValidation(options, {}, Validator.readContractId(contract)); 76 | response = await validator.validate(idToken); 77 | expect(response.result).toBeFalsy(response.detailedError); 78 | expect(response.detailedError).toEqual('Expected should have configuration issuers set for idToken'); 79 | expect(response.code).toEqual('VCSDKIDVa01'); 80 | 81 | // unmatched configuration endpoint 82 | validator = new IdTokenValidation(options, expected, Validator.readContractId(contract)); 83 | response = await validator.validate(new ClaimToken(TokenType.idToken, idToken.rawToken, 'https://example.com/wrong-config')); 84 | expect(response.result).toBeFalse(); 85 | expect(response.detailedError).toBeTruthy(); 86 | expect(response.code).toEqual('VCSDKIDVa05'); 87 | expect(response.status).toEqual(403); 88 | 89 | // todo fix aud 90 | }); 91 | }); -------------------------------------------------------------------------------- /tests/JsonWebSignatureToken.spec.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import base64url from 'base64url'; 6 | import { JsonWebSignatureToken, TokenPayload, ValidationError } from '../lib'; 7 | 8 | describe('JsonWebSignatureToken', () => { 9 | let knownJwt: string; 10 | let knownUnsecuredJwt: string; 11 | let header: TokenPayload; 12 | let payload: TokenPayload; 13 | let signature: string; 14 | 15 | beforeAll(() => { 16 | // test vector as defined in rfc https://tools.ietf.org/html/rfc7519#section-3.1 17 | knownJwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'; 18 | knownUnsecuredJwt = knownJwt.substring(0, knownJwt.lastIndexOf('.') + 1); 19 | signature = knownJwt.substring(knownJwt.lastIndexOf('.') + 1); 20 | header = { 21 | typ: "JWT", 22 | alg: 'HS256', 23 | }; 24 | 25 | payload = { 26 | iss: 'joe', 27 | exp: 1300819380, 28 | ['http://example.com/is_root']: true, 29 | }; 30 | }); 31 | 32 | describe('encode', () => { 33 | it('must encode input as jwt', () => { 34 | const token = JsonWebSignatureToken.encode(header, payload); 35 | expect(token.token).toEqual(knownUnsecuredJwt); 36 | }); 37 | }); 38 | 39 | describe('ctor', () => { 40 | it('must handle test vector', () => { 41 | const token = new JsonWebSignatureToken(knownJwt); 42 | expect(token.unsecured).toBeFalsy(); 43 | expect(token.signature).toEqual(signature); 44 | expect(token.header).toEqual(header); 45 | expect(token.payload).toEqual(payload); 46 | expect(token['preimageLength']).toEqual(knownUnsecuredJwt.length - 1); 47 | }); 48 | 49 | it('must handle unsecured jwt', () => { 50 | const token = new JsonWebSignatureToken(knownUnsecuredJwt); 51 | expect(token.unsecured).toEqual(true); 52 | expect(token.signature).toBeFalsy(); 53 | expect(token.header).toEqual(header); 54 | expect(token.payload).toEqual(payload); 55 | expect(token['preimageLength']).toEqual(knownUnsecuredJwt.length - 1); 56 | }); 57 | 58 | it('must reject unsecured jwt without trailing seperator', () => { 59 | try { 60 | new JsonWebSignatureToken(knownUnsecuredJwt.substring(0, knownUnsecuredJwt.length - 1)); 61 | fail('error expected'); 62 | } catch (error) { 63 | expect(error instanceof ValidationError).toBeTruthy(); 64 | } 65 | }); 66 | 67 | it('must reject malformed jwt without seperator', () => { 68 | try { 69 | new JsonWebSignatureToken('not a real token'); 70 | fail('error expected'); 71 | } catch (error) { 72 | expect(error instanceof ValidationError).toBeTruthy(); 73 | } 74 | }); 75 | 76 | it('must reject malformed header', () => { 77 | try { 78 | // this is not valid base64 79 | new JsonWebSignatureToken(`bad.${base64url.encode(JSON.stringify(payload))}.`); 80 | fail('error expected'); 81 | } catch (error) { 82 | expect(error instanceof ValidationError).toBeTruthy(); 83 | } 84 | }); 85 | 86 | it('must reject malformed payload', () => { 87 | try { 88 | // not a valid json object 89 | new JsonWebSignatureToken(`${base64url.encode(JSON.stringify(header))}.aqab.`); 90 | fail('error expected'); 91 | } catch (error) { 92 | expect(error instanceof ValidationError).toBeTruthy(); 93 | } 94 | }); 95 | }); 96 | }); -------------------------------------------------------------------------------- /tests/KeyvaultTest.spec.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import Credentials from './Credentials'; 6 | import { ClientSecretCredential } from '@azure/identity'; 7 | import { CryptoBuilder, JoseBuilder, KeyReference } from '../lib'; 8 | 9 | describe('Sample for Requestor with different key type and using Key Vault', () =>{ 10 | const credentials = new ClientSecretCredential(Credentials.tenantGuid, Credentials.clientId, Credentials.clientSecret); 11 | const keyVaultEnabled = Credentials.vaultUri.startsWith('https'); 12 | if(keyVaultEnabled) { 13 | console.log('To run this sample, you must specify your Key Vault credentials in Credentials.ts'); 14 | return; 15 | } 16 | let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; 17 | beforeAll(async () => { 18 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000; 19 | }); 20 | afterAll(() => { 21 | jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; 22 | }); 23 | 24 | it('should sign on key vault', async () => { 25 | const kvSigningKey = 'EC-Key-for-testing-remote-reference'; 26 | const kvRecoveryKey = 'EC-Key-for-testing-remote-reference-recovery'; 27 | const signingKeyReference = new KeyReference(kvSigningKey); 28 | const recoveryKeyReference = new KeyReference(kvRecoveryKey); 29 | let cryptoKv = new CryptoBuilder() 30 | .useSigningKeyReference(signingKeyReference) 31 | .useRecoveryKeyReference(recoveryKeyReference) 32 | .useKeyVault(credentials, Credentials.vaultUri) 33 | .build(); 34 | 35 | // Create request 36 | const payload = { 37 | purpose: 'payload to sign', 38 | action: 'make key vault swing' 39 | }; 40 | 41 | const jose = new JoseBuilder(cryptoKv).build(); 42 | //jose = await jose 43 | }); 44 | 45 | }); 46 | -------------------------------------------------------------------------------- /tests/LongFormDid.spec.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import { CryptoBuilder, IDidResolveResult, KeyReference, KeyUse, LongFormDid, ManagedHttpResolver, ValidatorBuilder } from '../lib'; 6 | import { ValidationHelpers } from '../lib/input_validation/ValidationHelpers'; 7 | 8 | 9 | describe('LongFormDid', () => { 10 | it('should generate and resolve a longform DID', async () => { 11 | let crypto = new CryptoBuilder() 12 | .useSigningKeyReference(new KeyReference('mars')) 13 | .useRecoveryKeyReference(new KeyReference('recovery')) 14 | .useUpdateKeyReference(new KeyReference('update')) 15 | .build(); 16 | crypto = await crypto.generateKey(KeyUse.Signature); 17 | crypto = await crypto.generateKey(KeyUse.Signature, 'recovery'); 18 | crypto = await crypto.generateKey(KeyUse.Signature, 'update'); 19 | 20 | const jwk: any = await crypto.builder.keyStore.get(crypto.builder.signingKeyReference); 21 | let did = await new LongFormDid(crypto).serialize(); 22 | expect(did.startsWith('did:ion')).toBeTruthy(); 23 | 24 | const validator = new ValidatorBuilder(crypto) 25 | .build(); 26 | 27 | // resolve DID 28 | const resolveResponse: IDidResolveResult = await validator.builder.resolver.resolve(did); 29 | expect(resolveResponse.didDocument.id).toEqual(did); 30 | let publicKey: any = ValidationHelpers.getPublicKeyFromDidDocument(resolveResponse.didDocument, `${did}#${crypto.builder.signingKeyReference.keyReference}`, did); 31 | expect(publicKey.x).toEqual(jwk.keys[0].x); 32 | expect(publicKey.y).toEqual(jwk.keys[0].y); 33 | expect(resolveResponse.didDocument.getServices()).toEqual([]); 34 | }); 35 | it('should add services to the longform DID', async () => { 36 | let crypto = new CryptoBuilder() 37 | .useSigningKeyReference(new KeyReference('mars')) 38 | .useRecoveryKeyReference(new KeyReference('recovery')) 39 | .useUpdateKeyReference(new KeyReference('update')) 40 | .build(); 41 | crypto = await crypto.generateKey(KeyUse.Signature); 42 | crypto = await crypto.generateKey(KeyUse.Signature, 'recovery'); 43 | crypto = await crypto.generateKey(KeyUse.Signature, 'update'); 44 | 45 | let did1 = await new LongFormDid(crypto).serialize(); 46 | let did2 = await new LongFormDid(crypto).serialize(); 47 | expect(did1).toEqual(did2); 48 | const services = { 49 | id: "service1Id", 50 | type: "service1Type", 51 | serviceEndpoint: "http://www.service1.com" 52 | } 53 | did2 = await new LongFormDid(crypto, [services]).serialize(); 54 | expect(did1).not.toEqual(did2); 55 | }); 56 | }); -------------------------------------------------------------------------------- /tests/ManagedHttpResolver.spec.ts: -------------------------------------------------------------------------------- 1 | import { ManagedHttpResolver } from '../lib/index'; 2 | import { DidDocument } from '@decentralized-identity/did-common-typescript'; 3 | import FetchRequest from '../lib/tracing/FetchRequest'; 4 | 5 | const fetchMock = require('fetch-mock'); 6 | 7 | describe('ManagedHttpResolver', () => { 8 | beforeAll(() => { 9 | fetchMock.reset(); 10 | }) 11 | afterAll(() => { 12 | fetchMock.reset(); 13 | }) 14 | 15 | it('should resolve', async () => { 16 | 17 | let called = false; 18 | 19 | fetchMock.get('https://resolver/did', (_url: string, opts: any) => { 20 | if (opts) { 21 | expect(opts.method).toEqual('GET'); 22 | called = true; 23 | } 24 | const didDocument = { 25 | didDocument: new DidDocument({ 26 | "@context": "https://w3id.org/did/v1", 27 | id: 'did', 28 | publicKey: [{ 29 | id: 'did', 30 | type: 'RsaVerificationKey2018', 31 | controller: 'did', 32 | publicKeyJwk: {} 33 | }] 34 | }) 35 | }; 36 | (didDocument.didDocument as any)['@context'] = 'https://w3id.org/did/v1'; 37 | return didDocument; 38 | }); 39 | 40 | const fetchRequest = new FetchRequest(); 41 | const resolver = new ManagedHttpResolver('https://resolver', fetchRequest); 42 | await resolver.resolve('did'); 43 | await resolver.resolve('did'); 44 | expect(called).toBeTruthy(); 45 | 46 | // Negative cases 47 | fetchMock.get('https://resolver/did', (_url: string, _opts: any) => { 48 | return { status: 404 }; 49 | }, { overwriteRoutes: true }); 50 | try { 51 | await resolver.resolve('did'); 52 | fail('exception on resolve was not thrown'); 53 | } catch (exception) { 54 | expect(exception.message).toEqual('Could not resolve https://resolver/did'); 55 | expect(exception.code).toEqual('VCSDKMARE01'); 56 | } 57 | }); 58 | }); -------------------------------------------------------------------------------- /tests/Requestor.spec.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import { KeyUse, RequestorBuilder } from '../lib'; 6 | import PresentationDefinition from './models/PresentationDefinitionSample1' 7 | 8 | describe('Requestor', () =>{ 9 | it('should create a requestor', async () => { 10 | const requestor = new RequestorBuilder(PresentationDefinition.presentationExchangeDefinition) 11 | .build(); 12 | 13 | // Generate key 14 | await requestor.builder.crypto.generateKey(KeyUse.Signature); 15 | const request = await requestor.create(); 16 | expect(request.result).toBeTruthy(); 17 | expect(requestor.payload.response_type).toEqual('id_token'); 18 | expect(requestor.payload.scope).toEqual('openid did_authn'); 19 | expect(requestor.payload.response_mode).toEqual('form_post'); 20 | expect(requestor.payload.client_id).toEqual('https://response.example.com'); 21 | expect(requestor.payload.redirect_uri).toEqual('https://response.example.com'); 22 | expect(requestor.audienceUrl()).toEqual('https://response.example.com'); 23 | 24 | }); 25 | it('should return trusted issuers', () => { 26 | const requestor = new RequestorBuilder(PresentationDefinition.presentationExchangeDefinition) 27 | .build(); 28 | 29 | expect(() => requestor.trustedIssuerConfigurationsForIdTokens()).toThrowMatching((exception) => exception.message === `Id Tokens only supported in Attestation Requestor model.` && exception.code === 'VCSDKRequ01'); 30 | expect(() => requestor.trustedIssuersForVerifiableCredentials()).toThrowMatching((exception) => exception.message === `trustedIssuersForVerifiableCredentials not supported for presentation exchange. Requires constraints.` && exception.code === 'VCSDKRequ02'); 31 | }) 32 | }); -------------------------------------------------------------------------------- /tests/SiopTokenValidator.spec.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { IExpectedSiop, IssuanceHelpers, SiopTokenValidator, TokenType, ValidationOptions } from '../lib'; 7 | import ValidationQueue from '../lib/input_validation/ValidationQueue'; 8 | import TestSetup from './TestSetup'; 9 | 10 | describe('SiopTokenValidator', () => { 11 | let setup: TestSetup; 12 | beforeEach(async () => { 13 | setup = new TestSetup(); 14 | }); 15 | 16 | afterEach(() => { 17 | setup.fetchMock.reset(); 18 | }); 19 | 20 | 21 | it('should test nonce and state', async () => { 22 | const [request, options, siop] = await IssuanceHelpers.createRequest(setup, TokenType.siopIssuance, true); 23 | const expected: IExpectedSiop = siop.expected.filter((token: IExpectedSiop) => token.type === TokenType.siopIssuance)[0]; 24 | 25 | let validator = new SiopTokenValidator(setup.validatorOptions, expected); 26 | let payload: any = { 27 | ...request.decodedToken, 28 | nonce: 'nonce', 29 | state: 'state' 30 | }; 31 | let siopRequest = await IssuanceHelpers.createSiopRequestWithPayload(setup, payload, siop.didJwkPrivate); 32 | let queue = new ValidationQueue(); 33 | queue.enqueueToken('siop', siopRequest); 34 | let response = await validator.validate(queue, queue.getNextToken()!); 35 | expect(response.result).toBeTruthy(); 36 | 37 | expected.nonce = 'nonce'; 38 | expected.state = 'state'; 39 | validator = new SiopTokenValidator(setup.validatorOptions, expected); 40 | siopRequest = await IssuanceHelpers.createSiopRequestWithPayload(setup, payload, siop.didJwkPrivate); 41 | queue = new ValidationQueue(); 42 | queue.enqueueToken('siop', siopRequest); 43 | const queudToken = queue.getNextToken(); 44 | expect(queudToken?.isUnderValidation).toBeFalsy(); 45 | response = await validator.validate(queue, queudToken!); 46 | expect(response.result).toBeTruthy(); 47 | 48 | // negative cases 49 | // wrong state in response 50 | payload = { 51 | ...request.decodedToken, 52 | nonce: 'nonce', 53 | state: 'xxx' 54 | }; 55 | siopRequest = await IssuanceHelpers.createSiopRequestWithPayload(setup, payload, siop.didJwkPrivate); 56 | queue = new ValidationQueue(); 57 | queue.enqueueToken('siop', siopRequest); 58 | response = await validator.validate(queue, queue.getNextToken()!); 59 | expect(response.result).toBeFalsy(); 60 | expect(response.detailedError).toEqual(`Expected state 'state' does not match 'xxx'.`); 61 | expect(response.code).toEqual('VCSDKSTVa02'); 62 | 63 | // wrong state in response 64 | payload = { 65 | ...request.decodedToken, 66 | nonce: 'xxx', 67 | state: 'state' 68 | }; 69 | siopRequest = await IssuanceHelpers.createSiopRequestWithPayload(setup, payload, siop.didJwkPrivate); 70 | queue = new ValidationQueue(); 71 | queue.enqueueToken('siop', siopRequest); 72 | response = await validator.validate(queue, queue.getNextToken()!); 73 | expect(response.result).toBeFalsy(); 74 | expect(response.detailedError).toEqual(`Expected nonce 'nonce' does not match 'xxx'.`); 75 | expect(response.code).toEqual('VCSDKSTVa01'); 76 | }); 77 | 78 | }); -------------------------------------------------------------------------------- /tests/SiopValidationSimulation.spec.ts: -------------------------------------------------------------------------------- 1 | import { ValidatorBuilder } from '../lib/index'; 2 | import TestSetup from './TestSetup'; 3 | import ValidationQueue from '../lib/input_validation/ValidationQueue'; 4 | import SiopValidationSimulation from './SiopValidationSimulation'; 5 | 6 | describe('SiopValidationSimulation', () => { 7 | let setup: TestSetup; 8 | beforeEach(async () => { 9 | setup = new TestSetup(); 10 | setup.fetchMock.reset(); 11 | await setup.generateKeys(); 12 | }); 13 | afterEach(async () => { 14 | setup.fetchMock.reset(); 15 | }); 16 | }); -------------------------------------------------------------------------------- /tests/ValidationSafeguards.spec.ts: -------------------------------------------------------------------------------- 1 | import { ValidationSafeguards } from "../lib"; 2 | 3 | 4 | describe('ValidationSafeguards', () => { 5 | it('should instantiate', () => { 6 | let safeguards = new ValidationSafeguards(); 7 | expect(safeguards.maxNumberOfIdTokensInSiop).toEqual(5); 8 | expect(safeguards.maxNumberOfVCTokensInPresentation).toEqual(1); 9 | expect(safeguards.maxNumberOfVPTokensInSiop).toEqual(10); 10 | expect(safeguards.maxSizeOfIdToken).toEqual(16*1024*1024); 11 | expect(safeguards.maxSizeOfVCTokensInPresentation).toEqual(16*1024*1024); 12 | expect(safeguards.maxSizeOfVPTokensInSiop).toEqual(16*1024*1024); 13 | 14 | // set individual props 15 | safeguards = new ValidationSafeguards({maxNumberOfIdTokensInSiop: 10}); 16 | expect(safeguards.maxNumberOfIdTokensInSiop).toEqual(10); 17 | safeguards = new ValidationSafeguards({maxNumberOfVCTokensInPresentation: 20}); 18 | expect(safeguards.maxNumberOfVCTokensInPresentation).toEqual(20); 19 | safeguards = new ValidationSafeguards({maxNumberOfVPTokensInSiop: 30}); 20 | expect(safeguards.maxNumberOfVPTokensInSiop).toEqual(30); 21 | safeguards = new ValidationSafeguards({maxSizeOfIdToken: 1000}); 22 | expect(safeguards.maxSizeOfIdToken).toEqual(1000); 23 | safeguards = new ValidationSafeguards({maxSizeOfVCTokensInPresentation: 2000}); 24 | expect(safeguards.maxSizeOfVCTokensInPresentation).toEqual(2000); 25 | safeguards = new ValidationSafeguards({maxSizeOfVPTokensInSiop: 3000}); 26 | expect(safeguards.maxSizeOfVPTokensInSiop).toEqual(3000); 27 | }); 28 | 29 | it('should modify state', () => { 30 | let safeguards = new ValidationSafeguards(); 31 | safeguards.maxNumberOfIdTokensInSiop = 20; 32 | expect(safeguards.maxNumberOfIdTokensInSiop).toEqual(20); 33 | safeguards.maxNumberOfVCTokensInPresentation = 25; 34 | expect(safeguards.maxNumberOfVCTokensInPresentation).toEqual(25); 35 | safeguards.maxNumberOfVPTokensInSiop = 30; 36 | expect(safeguards.maxNumberOfVPTokensInSiop).toEqual(30); 37 | safeguards.maxSizeOfIdToken = 40; 38 | expect(safeguards.maxSizeOfIdToken).toEqual(40); 39 | safeguards.maxSizeOfVCTokensInPresentation = 50; 40 | expect(safeguards.maxSizeOfVCTokensInPresentation).toEqual(50); 41 | safeguards.maxSizeOfVPTokensInSiop = 60; 42 | expect(safeguards.maxSizeOfVPTokensInSiop).toEqual(60); 43 | }); 44 | }); -------------------------------------------------------------------------------- /tests/ValidatorBuilder.spec.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { Crypto, ValidatorBuilder, CryptoBuilder, ManagedHttpResolver, RequestorBuilder, BasicValidatorOptions, FetchRequest } from '../lib/index'; 7 | 8 | describe('ValidatorBuilder', () => { 9 | it('should test status feature flag', () => { 10 | const options = new BasicValidatorOptions(); 11 | let builder = new ValidatorBuilder(options.crypto); 12 | expect(builder.featureVerifiedCredentialsStatusCheckEnabled).toBeTruthy(); 13 | 14 | builder = builder.enableFeatureVerifiedCredentialsStatusCheck(false); 15 | expect(builder.featureVerifiedCredentialsStatusCheckEnabled).toBeFalsy(); 16 | }); 17 | 18 | it('should return validationOptions', () => { 19 | const resolver = new ManagedHttpResolver('https://example.com'); 20 | const fetchRequest = new FetchRequest(); 21 | const options = new BasicValidatorOptions(); 22 | let builder = new ValidatorBuilder(options.crypto) 23 | .useFetchRequest(fetchRequest) 24 | .useMaxSizeOfVPTokensInSiop(options.validationSafeguards.maxSizeOfVPTokensInSiop) 25 | .useResolver(resolver); 26 | expect(builder.crypto).toEqual(builder.validationOptions.crypto); 27 | expect(builder.fetchRequest).toEqual(builder.validationOptions.fetchRequest); 28 | expect(builder.resolver).toEqual(builder.validationOptions.resolver); 29 | }); 30 | 31 | it('should use new resolver', () => { 32 | let options = new BasicValidatorOptions(); 33 | let builder = new ValidatorBuilder(options.crypto); 34 | let resolver = builder.resolver; 35 | builder.useResolver(new ManagedHttpResolver('https://resolver.example.com')); 36 | expect(builder.resolver).not.toEqual(resolver); 37 | 38 | resolver = new ManagedHttpResolver('https://resolver.example.com'); 39 | options = new BasicValidatorOptions(resolver); 40 | builder = new ValidatorBuilder(options.crypto); 41 | expect(resolver).toEqual(options.resolver); 42 | expect(options.fetchRequest).toBeDefined(); 43 | }); 44 | 45 | it('should set state', () => { 46 | const crypto = new CryptoBuilder().build(); 47 | let builder = new ValidatorBuilder(crypto); 48 | expect(builder.state).toBeUndefined(); 49 | 50 | builder = new ValidatorBuilder(crypto) 51 | .useState('12345'); 52 | expect(builder.state).toEqual('12345'); 53 | 54 | let requestor = new RequestorBuilder({}) 55 | .useState('abcdef'); 56 | builder = new ValidatorBuilder(crypto) 57 | .useState('12345'); 58 | expect(builder.state).toEqual('12345'); 59 | }); 60 | 61 | it('should set nonce', () => { 62 | const crypto = new CryptoBuilder().build(); 63 | let builder = new ValidatorBuilder(crypto); 64 | expect(builder.nonce).toBeUndefined(); 65 | 66 | builder = new ValidatorBuilder(crypto) 67 | .useNonce('12345'); 68 | expect(builder.nonce).toEqual('12345'); 69 | 70 | let requestor = new RequestorBuilder({}) 71 | .useNonce('abcdef'); 72 | builder = new ValidatorBuilder(crypto) 73 | .useNonce('12345') 74 | expect(builder.nonce).toEqual('12345'); 75 | }); 76 | 77 | it('should modify validation safeguards', () => { 78 | const crypto = new CryptoBuilder().build(); 79 | let builder = new ValidatorBuilder(crypto); 80 | 81 | //builder.useMaxNumberOfIdTokensInSiop = 20; 82 | //expect(safeguards.maxNumberOfIdTokensInSiop).toEqual(20); 83 | builder.useMaxNumberOfVCTokensInPresentation(25); 84 | expect(builder.maxNumberOfVCTokensInPresentation).toEqual(25); 85 | //builder.useMaxNumberOfVPTokensInSiop = 30; 86 | //expect(builder.maxNumberOfVPTokensInSiop).toEqual(30); 87 | builder.useMaxSizeOfIdToken(40); 88 | expect(builder.maxSizeOfIdToken).toEqual(40); 89 | builder.useMaxSizeOfVCTokensInPresentation(50); 90 | expect(builder.maxSizeOfVCTokensInPresentation).toEqual(50); 91 | builder.useMaxSizeOfVPTokensInSiop(60); 92 | expect(builder.maxSizeOfVPTokensInSiop).toEqual(60); 93 | }); 94 | 95 | }); 96 | -------------------------------------------------------------------------------- /tests/VerifiablePresentationAttestationModel.spec.ts: -------------------------------------------------------------------------------- 1 | import { RulesValidationError, VerifiablePresentationAttestationModel } from '../lib'; 2 | 3 | describe('VerifiablePresentationAttestationModel', () => { 4 | let vpAttestationModelInput: any; 5 | 6 | beforeAll(() => { 7 | vpAttestationModelInput = { 8 | mapping: { claimMapping1: { type: 'String', claim: '$.claim1' } }, 9 | credentialType: 'VcType', 10 | validityInterval: 1343413, 11 | }; 12 | }); 13 | 14 | describe('populateFrom()', () => { 15 | it('should fail with undefined trusted issuer DID', () => { 16 | const undefinedDidIssuers = [{ iss: 'did:ion:issuer1' }, {}]; 17 | const vpAttestationModel = new VerifiablePresentationAttestationModel(); 18 | 19 | try { 20 | vpAttestationModel.populateFrom({ ...vpAttestationModelInput, issuers: undefinedDidIssuers }); 21 | fail('No error was thrown.'); 22 | } catch (error) { 23 | expect(error instanceof RulesValidationError).toEqual(true); 24 | } 25 | }); 26 | 27 | it('should fail with invalid trusted issuer DID', () => { 28 | const trivialDidIssuers = [{ iss: 'did:ion:issuer1' }, { iss: '' }]; 29 | const vpAttestationModel = new VerifiablePresentationAttestationModel(); 30 | 31 | try { 32 | vpAttestationModel.populateFrom({ ...vpAttestationModelInput, issuers: trivialDidIssuers }); 33 | fail('No error was thrown.'); 34 | } catch (error) { 35 | expect(error instanceof RulesValidationError).toEqual(true); 36 | } 37 | }); 38 | 39 | it('should fail with non-string trusted issuer DID', () => { 40 | const nonStringDidIssuers = [{ iss: 1 }]; 41 | const vpAttestationModel = new VerifiablePresentationAttestationModel(); 42 | 43 | try { 44 | vpAttestationModel.populateFrom({ ...vpAttestationModelInput, issuers: nonStringDidIssuers }); 45 | fail('No error was thrown.'); 46 | } catch (error) { 47 | expect(error instanceof RulesValidationError).toEqual(true); 48 | } 49 | }); 50 | }); 51 | }); -------------------------------------------------------------------------------- /tests/VerifiablePresentationStatusReceipt.spec.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { CryptoBuilder, DidValidation, IExpectedStatusReceipt, TokenType, ValidationOptions, ValidatorBuilder, VerifiablePresentationStatusReceipt } from '../lib/index'; 7 | 8 | describe('VerifiablePresentationStatusReceipt', () => { 9 | it('should test validate', async () => { 10 | let receipts = {}; 11 | let validatorBuilder = new ValidatorBuilder(new CryptoBuilder().build()); 12 | let validationOptions = new ValidationOptions({ invalidTokenError: ValidatorBuilder.INVALID_TOKEN_STATUS_CODE }, TokenType.verifiablePresentationStatus); 13 | let expected: IExpectedStatusReceipt = { 14 | didAudience: 'didAudience', 15 | didIssuer: 'issuer', 16 | type: TokenType.verifiablePresentationStatus 17 | }; 18 | 19 | let verifiablePresentationStatusReceipt = new VerifiablePresentationStatusReceipt(receipts, validatorBuilder, validationOptions, expected); 20 | expect(verifiablePresentationStatusReceipt.verifiablePresentationStatus).toBeUndefined(); 21 | 22 | try { 23 | await verifiablePresentationStatusReceipt.validate(); 24 | } catch (exception) { 25 | expect(exception.message).toEqual('The status receipt is missing receipt'); 26 | expect(exception.code).toEqual('VCSDKVPSC01'); 27 | } 28 | let validator = verifiablePresentationStatusReceipt.didValidation; 29 | expect(validator.constructor.name).toEqual('DidValidation'); 30 | 31 | validator = new DidValidation(validationOptions, expected); 32 | let validatorSpy = spyOn(validator, "validate").and.callFake(() => { 33 | return { result: false, status: ValidatorBuilder.INVALID_TOKEN_STATUS_CODE }; 34 | }); 35 | 36 | verifiablePresentationStatusReceipt = new VerifiablePresentationStatusReceipt({ receipt: [{}] }, validatorBuilder, validationOptions, expected); 37 | verifiablePresentationStatusReceipt.didValidation = validator; 38 | let response = await verifiablePresentationStatusReceipt.validate(); 39 | expect(response.result).toBeFalsy(response.detailedError); 40 | expect(response.status).toEqual(ValidatorBuilder.INVALID_TOKEN_STATUS_CODE); 41 | 42 | validatorSpy.and.callFake(() => { 43 | return { result: true, payloadObject: { aud: '' } }; 44 | }); 45 | response = await verifiablePresentationStatusReceipt.validate(); 46 | expect(response.result).toBeFalsy(response.detailedError); 47 | expect(response.status).toEqual(ValidatorBuilder.INVALID_TOKEN_STATUS_CODE); 48 | 49 | validatorSpy.and.callFake(() => { 50 | return { result: true, payloadObject: { aud: 'didAudience', issuer: '' } }; 51 | }); 52 | response = await verifiablePresentationStatusReceipt.validate(); 53 | expect(response.result).toBeFalsy(response.detailedError); 54 | expect(response.status).toEqual(ValidatorBuilder.INVALID_TOKEN_STATUS_CODE); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /tests/helpers/extendDebuggingTimeout.js: -------------------------------------------------------------------------------- 1 | if (/--debug|--inspect/.test(process.execArgv.join(' '))) { 2 | console.log('Node process is under debugger; extending test timeout.'); 3 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 60 * 60 * 24; // seconds 4 | } 5 | -------------------------------------------------------------------------------- /tests/helpers/reporter.js: -------------------------------------------------------------------------------- 1 | // this is a Jasmine helper function used to export results as xunit tests results. 2 | var jasmineReporters = require('jasmine-reporters'); 3 | var SpecReporter = require('jasmine-spec-reporter').SpecReporter; 4 | 5 | var junitReporter = new jasmineReporters.NUnitXmlReporter({ 6 | savePath: './', 7 | consolidateAll: false, 8 | }); 9 | 10 | var textReporter = new SpecReporter({ // add jasmine-spec-reporter 11 | spec: { 12 | displayDuration: true, 13 | } 14 | }); 15 | 16 | jasmine.getEnv().clearReporters(); 17 | jasmine.getEnv().addReporter(junitReporter); 18 | jasmine.getEnv().addReporter(textReporter); -------------------------------------------------------------------------------- /tests/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "./tests", 3 | "spec_files": [ 4 | "./**/*[sS]pec.ts" 5 | ], 6 | "helpers": [ 7 | "./helpers/**/*.js" 8 | ], 9 | "stopSpecOnExpectationFailure": false, 10 | "random": false 11 | } 12 | -------------------------------------------------------------------------------- /tests/models/ITestModel.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface to model your presentation exchange request 3 | */ 4 | 5 | import { IRequestorPresentationExchange, ClaimToken, IRequestorAttestation } from "../../lib"; 6 | 7 | 8 | export default interface ITestModel { 9 | clientId: string; 10 | 11 | /** 12 | * Define the model for the request 13 | */ 14 | request: IRequestorPresentationExchange | IRequestorAttestation; 15 | 16 | /** 17 | * Define the model for the response 18 | */ 19 | response: any; 20 | 21 | /** 22 | * Define the status response 23 | */ 24 | responseStatus: any; 25 | 26 | /** 27 | * Define a set of operations to perform on response payload before signing the SIOP 28 | */ 29 | preSiopResponseOperations?: any[]; 30 | 31 | /** 32 | * Define a set of operations to perform on response payload before signing the tokens 33 | */ 34 | preSignatureResponseOperations?: any[]; 35 | 36 | /** 37 | * Retrieve VC 38 | * @param key of the VC 39 | */ 40 | getVcFromResponse(key: string): ClaimToken | undefined; 41 | 42 | /** 43 | * Return all presentations 44 | */ 45 | getPresentationsFromModel(): {[key: string]: any} | undefined; 46 | 47 | /** 48 | * Return all non presented VCs 49 | */ 50 | getNonPresentedVcFromModel(): {[key: string]: any}; 51 | 52 | /** 53 | * Return all id tokens from model 54 | */ 55 | getIdTokensFromModel(): {[key: string]: any}; 56 | } -------------------------------------------------------------------------------- /tests/models/PresentationDefinitionSample1.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your presentation exchange request 3 | */ 4 | 5 | import { PresentationDefinitionModel } from '../../lib'; 6 | 7 | export default class PresentationDefinition { 8 | 9 | public static presentationExchangeDefinition: any = { 10 | clientId: 'https://requestor.example.com', 11 | clientName: 'My relying party', 12 | clientPurpose: 'Need your VC to provide access', 13 | redirectUri: 'https://response.example.com', 14 | tosUri: 'https://tosUri.example.com', 15 | logoUri: 'https://logoUri.example.com', 16 | 17 | presentationDefinition: { 18 | name: 'Get identity card', 19 | purpose: 'Needed to provide you access to the site', 20 | input_descriptors: [ 21 | { 22 | id: 'IdentityCard', 23 | schema: { 24 | uri: ['https://schema.org/IdentityCardCredential'], 25 | name: 'IdentityCard', 26 | purpose: 'Testing the site' 27 | }, 28 | issuance: [ 29 | { 30 | manifest: 'https://portableidentitycards.azure-api.net/dev-v1.0/536279f6-15cc-45f2-be2d-61e352b51eef/portableIdentities/contracts/IdentityCard1' 31 | } 32 | ] 33 | } 34 | ] 35 | } 36 | 37 | } 38 | } -------------------------------------------------------------------------------- /tests/models/RequestAttestationsNameTagOk.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your SIOP request 3 | */ 4 | 5 | import ITestModel from './ITestModel'; 6 | import { ClaimToken } from '../../lib'; 7 | 8 | 9 | export default class RequestAttestationsNameTagOk implements ITestModel { 10 | public clientId = 'https://requestor.example.com'; 11 | 12 | /** 13 | * Define the model for the request 14 | */ 15 | public request: any = { 16 | clientId: this.clientId, 17 | clientName: 'My relying party', 18 | clientPurpose: 'Need your VC to provide access', 19 | redirectUri: this.clientId, 20 | tosUri: 'https://tosUri.example.com', 21 | logoUri: 'https://logoUri.example.com', 22 | attestations: { 23 | selfIssued: { 24 | mapping: { 25 | alias: { 26 | claim: 'name', 27 | type: 'string', 28 | required: false, 29 | indexed: false 30 | } 31 | } 32 | } 33 | }, 34 | } 35 | 36 | 37 | /** 38 | * Define the model for the response 39 | */ 40 | public response: any = { 41 | iss: 'https://self-issued.me', 42 | aud: this.clientId, 43 | nonce: '', 44 | state: '', 45 | did: '', 46 | jti: 'fa8fdc8f-d95b-4237-9c90-9696112f4e19', 47 | attestations: { 48 | selfIssued: { 49 | name: 'Jules Winnfield', 50 | birthDate: '1948-21-21T00:00:00' 51 | }, 52 | }, 53 | } 54 | 55 | public responseStatus = { 56 | }; 57 | 58 | /** 59 | * Return a specific VC 60 | * @param key Name for the vc 61 | */ 62 | public getVcFromResponse(_key: string): ClaimToken | undefined { 63 | return undefined; 64 | } 65 | 66 | /** 67 | * Return all presentations from model 68 | */ 69 | public getPresentationsFromModel(): { [key: string]: any } | undefined { 70 | return undefined; 71 | } 72 | 73 | /** 74 | * Return all non presented VCs 75 | */ 76 | public getNonPresentedVcFromModel(): { [key: string]: any } { 77 | return {}; 78 | } 79 | 80 | /** 81 | * Return all id tokens from model 82 | */ 83 | public getIdTokensFromModel(): { [key: string]: any } { 84 | return {}; 85 | } 86 | } -------------------------------------------------------------------------------- /tests/models/RequestAttestationsOneSelfAssertedResponseOk .ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your SIOP request 3 | */ 4 | 5 | import ITestModel from './ITestModel'; 6 | import { ClaimToken } from '../../lib'; 7 | 8 | 9 | export default class RequestAttestationsOneSelfAssertedResponseOk implements ITestModel { 10 | public clientId = 'https://requestor.example.com'; 11 | 12 | /** 13 | * Define the model for the request 14 | */ 15 | public request: any = { 16 | clientId: this.clientId, 17 | clientName: 'My relying party', 18 | clientPurpose: 'Need your VC to provide access', 19 | redirectUri: this.clientId, 20 | tosUri: 'https://tosUri.example.com', 21 | logoUri: 'https://logoUri.example.com', 22 | attestations: { 23 | selfIssued: { 24 | mapping: { 25 | alias: { 26 | claim: 'name', 27 | type: 'string', 28 | required: false, 29 | indexed: false 30 | } 31 | } 32 | } 33 | } 34 | } 35 | 36 | 37 | /** 38 | * Define the model for the response 39 | */ 40 | public response: any = { 41 | iss: 'https://self-issued.me', 42 | aud: this.clientId, 43 | nonce: '', 44 | state: '', 45 | did: '', 46 | jti: 'fa8fdc8f-d95b-4237-9c90-9696112f4e19', 47 | attestations: { 48 | selfIssued: { 49 | name: 'Jules Winnfield', 50 | birthDate: '1948-21-21T00:00:00' 51 | } 52 | } 53 | } 54 | 55 | public responseStatus = {}; 56 | 57 | /** 58 | * Return a specific VC 59 | * @param key Name for the vc 60 | */ 61 | public getVcFromResponse(): ClaimToken | undefined { 62 | return undefined; 63 | } 64 | 65 | /** 66 | * Return all presentations from model 67 | */ 68 | public getPresentationsFromModel(): { [key: string]: any } { 69 | return {}; 70 | } 71 | 72 | /** 73 | * Return all non presented VCs 74 | */ 75 | public getNonPresentedVcFromModel(): { [key: string]: any } { 76 | return {}; 77 | } 78 | 79 | /** 80 | * Return all id tokens from model 81 | */ 82 | public getIdTokensFromModel(): { [key: string]: any } { 83 | return this.response.attestations.idTokens;{} 84 | } 85 | } -------------------------------------------------------------------------------- /tests/models/RequestAttestationsOneVcSaIdtokenResponseNoIdToken.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your presentation exchange request 3 | */ 4 | 5 | import ITestModel from './ITestModel'; 6 | import RequestAttestationsOneVcSaIdtokenResponseOk from './RequestAttestationsOneVcSaIdtokenResponseOk'; 7 | 8 | 9 | export default class RequestAttestationsOneVcSaIdtokenResponseNoIdToken extends RequestAttestationsOneVcSaIdtokenResponseOk implements ITestModel { 10 | 11 | public preSiopResponseOperations = [ 12 | { 13 | path: '$.attestations.idTokens', 14 | operation: () => undefined 15 | }]; 16 | } -------------------------------------------------------------------------------- /tests/models/RequestAttestationsOneVcSaIdtokenResponseOne.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your presentation exchange request 3 | */ 4 | 5 | import ITestModel from './ITestModel'; 6 | import RequestAttestationsOneVcSaIdtokenResponseOk from './RequestAttestationsOneVcSaIdtokenResponseOk'; 7 | 8 | 9 | export default class RequestAttestationsOneVcSaIdtokenResponseOne extends RequestAttestationsOneVcSaIdtokenResponseOk implements ITestModel { 10 | 11 | public preSiopResponseOperations = [ 12 | { 13 | path: '$.attestations.presentations.DriversLicenseCredential', 14 | operation: () => undefined 15 | }]; 16 | } -------------------------------------------------------------------------------- /tests/models/RequestOneJsonLdVcResponseNoProofInVC.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your presentation exchange request 3 | */ 4 | 5 | import ITestModel from "./ITestModel"; 6 | import RequestOneJsonLdVcResponseOk from "./RequestOneJsonLdVcResponseOk"; 7 | 8 | 9 | export default class RequestOneJsonLdVcResponseNoProofInVC extends RequestOneJsonLdVcResponseOk implements ITestModel { 10 | public preSiopResponseOperations = [ 11 | { 12 | path: '$.presentation_submission.attestations.presentations.IdentityCard.verifiableCredential.proof', 13 | operation: () => undefined 14 | }]; 15 | } -------------------------------------------------------------------------------- /tests/models/RequestOneJsonLdVcResponseOk.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your presentation exchange request 3 | */ 4 | 5 | import ITestModel from "./ITestModel"; 6 | import { ClaimToken, TokenType } from "../../lib"; 7 | 8 | 9 | export default class RequestOneJsonLdVcResponseOk implements ITestModel { 10 | public clientId = 'https://requestor.example.com'; 11 | 12 | /** 13 | * Define the model for the request 14 | */ 15 | public request: any = { 16 | clientId: this.clientId, 17 | clientName: 'My relying party', 18 | clientPurpose: 'Need your VC to provide access', 19 | redirectUri: this.clientId, 20 | tosUri: 'https://tosUri.example.com', 21 | logoUri: 'https://logoUri.example.com', 22 | 23 | presentationDefinition: { 24 | name: 'Get identity card', 25 | purpose: 'Needed to provide you access to the site', 26 | input_descriptors: [ 27 | { 28 | id: 'IdentityCard', 29 | schema: { 30 | uri: ['https://schema.org/IdentityCardCredential'], 31 | name: 'IdentityCard', 32 | purpose: 'Testing the site' 33 | }, 34 | issuance: [ 35 | { 36 | manifest: 'https://portableidentitycards.azure-api.net/dev-v1.0/536279f6-15cc-45f2-be2d-61e352b51eef/portableIdentities/contracts/IdentityCard1' 37 | } 38 | ] 39 | } 40 | ] 41 | } 42 | } 43 | 44 | 45 | /** 46 | * Define the model for the response 47 | */ 48 | public response: any = { 49 | iss: 'https://self-issued.me', 50 | aud: this.clientId, 51 | nonce: '', 52 | state: '', 53 | did: '', 54 | jti: 'fa8fdc8f-d95b-4237-9c90-9696112f4e19', 55 | presentation_submission: { 56 | descriptor_map: [ 57 | { 58 | id: 'IdentityCard', 59 | format: 'jwt', 60 | encoding: 'base64Url', 61 | path: '$.presentation_submission.attestations.presentations.IdentityCard.verifiableCredential' 62 | } 63 | ], 64 | attestations: { 65 | presentations: { 66 | IdentityCard: { 67 | 'id': `urn:pic:1`, 68 | 'expirationDate': 'Wed, 14 Jun 3017 07:00:00 GMT', 69 | 'verifier': this.clientId, 70 | '\@context': [ 71 | 'https://www.w3.org/2018/credentials/v1', 72 | 'https://portableidentitycards.azure-api.net/42b39d9d-0cdd-4ae0-b251-b7b39a561f91/api/portable/v1.0/contracts/test/schema' 73 | ], 74 | 'type': [ 75 | 'VerifiableCredential', 76 | 'IdentityCard' 77 | ], 78 | 'credentialSubject': { 79 | givenName: 'Jules', 80 | familyName: 'Winnfield', 81 | profession: 'hitman' 82 | }, 83 | 'credentialStatus': { 84 | 'id': `https://status.example.com`, 85 | 'type': 'PortableIdentityCardServiceCredentialStatus2020' 86 | } 87 | }, 88 | }, 89 | } 90 | 91 | }, 92 | } 93 | 94 | public responseStatus = { 95 | 'IdentityCard': { 96 | 'aud': 'did:ion:EiBcPY...', 97 | 'credentialStatus': { 98 | 'status': 'valid', 99 | 'reason': `I don't like them` 100 | } 101 | } 102 | }; 103 | 104 | /** 105 | * Return a specific VC 106 | * @param key Name for the vc 107 | */ 108 | public getVcFromResponse(key: string): ClaimToken { 109 | // Decode de presentation 110 | let claimToken = ClaimToken.create(this.response.presentation_submission.attestations.presentations[key].verifiableCredential); 111 | return claimToken; 112 | } 113 | 114 | 115 | /** 116 | * Return all presentations 117 | */ 118 | public getPresentationsFromModel(): { [key: string]: any } { 119 | return this.response.presentation_submission.attestations.presentations; 120 | } 121 | 122 | /** 123 | * Return all non presented VCs 124 | */ 125 | public getNonPresentedVcFromModel(): { [key: string]: any } { 126 | return {}; 127 | } 128 | 129 | /** 130 | * Return all id tokens from model 131 | */ 132 | public getIdTokensFromModel(): { [key: string]: any } { 133 | return {}; 134 | } 135 | } -------------------------------------------------------------------------------- /tests/models/RequestOneJsonLdVcResponseWrongSiopDId.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your presentation exchange request 3 | */ 4 | 5 | import ITestModel from './ITestModel'; 6 | import RequestOneJsonLdVcResponseOk from './RequestOneJsonLdVcResponseOk'; 7 | 8 | 9 | export default class RequestOneJsonLdVcResponseWrongSiopDId extends RequestOneJsonLdVcResponseOk implements ITestModel { 10 | 11 | public preSiopResponseOperations = [ 12 | { 13 | path: '$.presentation_submission.attestations.presentations.IdentityCard.verifiableCredential.credentialSubject.id', 14 | operation: () => 'wrong did' 15 | } 16 | ]; 17 | } -------------------------------------------------------------------------------- /tests/models/RequestOneJsonLdVcTwoSubjectsResponseOk.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your presentation exchange request 3 | */ 4 | 5 | import ITestModel from "./ITestModel"; 6 | import { ClaimToken, TokenType } from "../../lib"; 7 | 8 | 9 | export default class RequestOneJsonLdVcResponseOk implements ITestModel { 10 | public clientId = 'https://requestor.example.com'; 11 | 12 | /** 13 | * Define the model for the request 14 | */ 15 | public request: any = { 16 | clientId: this.clientId, 17 | clientName: 'My relying party', 18 | clientPurpose: 'Need your VC to provide access', 19 | redirectUri: this.clientId, 20 | tosUri: 'https://tosUri.example.com', 21 | logoUri: 'https://logoUri.example.com', 22 | 23 | presentationDefinition: { 24 | name: 'Get identity card', 25 | purpose: 'Needed to provide you access to the site', 26 | input_descriptors: [ 27 | { 28 | id: 'IdentityCard', 29 | schema: { 30 | uri: ['https://schema.org/IdentityCardCredential'], 31 | name: 'IdentityCard', 32 | purpose: 'Testing the site' 33 | }, 34 | issuance: [ 35 | { 36 | manifest: 'https://portableidentitycards.azure-api.net/dev-v1.0/536279f6-15cc-45f2-be2d-61e352b51eef/portableIdentities/contracts/IdentityCard1' 37 | } 38 | ] 39 | } 40 | ] 41 | } 42 | } 43 | 44 | 45 | /** 46 | * Define the model for the response 47 | */ 48 | public response: any = { 49 | iss: 'https://self-issued.me', 50 | aud: this.clientId, 51 | nonce: '', 52 | state: '', 53 | did: '', 54 | jti: 'fa8fdc8f-d95b-4237-9c90-9696112f4e19', 55 | presentation_submission: { 56 | descriptor_map: [ 57 | { 58 | id: 'IdentityCard', 59 | format: 'jwt', 60 | encoding: 'base64Url', 61 | path: '$.presentation_submission.attestations.presentations.IdentityCard.verifiableCredential' 62 | } 63 | ], 64 | attestations: { 65 | presentations: { 66 | IdentityCard: { 67 | 'id': `urn:pic:1`, 68 | 'expirationDate': 'Wed, 14 Jun 3017 07:00:00 GMT', 69 | 'verifier': this.clientId, 70 | '\@context': [ 71 | 'https://www.w3.org/2018/credentials/v1', 72 | 'https://portableidentitycards.azure-api.net/42b39d9d-0cdd-4ae0-b251-b7b39a561f91/api/portable/v1.0/contracts/test/schema' 73 | ], 74 | 'type': [ 75 | 'VerifiableCredential', 76 | 'IdentityCard' 77 | ], 78 | 'credentialSubject': [{ 79 | givenName: 'Jules', 80 | familyName: 'Winnfield', 81 | profession: 'hitman' 82 | }, 83 | { 84 | age: '58' 85 | }], 86 | 'credentialStatus': { 87 | 'id': `https://status.example.com`, 88 | 'type': 'PortableIdentityCardServiceCredentialStatus2020' 89 | } 90 | }, 91 | }, 92 | } 93 | 94 | }, 95 | } 96 | 97 | public responseStatus = { 98 | 'IdentityCard': { 99 | 'aud': 'did:ion:EiBcPY...', 100 | 'credentialStatus': { 101 | 'status': 'valid', 102 | 'reason': `I don't like them` 103 | } 104 | } 105 | }; 106 | 107 | /** 108 | * Return a specific VC 109 | * @param key Name for the vc 110 | */ 111 | public getVcFromResponse(key: string): ClaimToken { 112 | // Decode de presentation 113 | let claimToken = ClaimToken.create(this.response.presentation_submission.attestations.presentations[key].verifiableCredential); 114 | return claimToken; 115 | } 116 | 117 | 118 | /** 119 | * Return all presentations 120 | */ 121 | public getPresentationsFromModel(): { [key: string]: any } { 122 | return this.response.presentation_submission.attestations.presentations; 123 | } 124 | 125 | /** 126 | * Return all non presented VCs 127 | */ 128 | public getNonPresentedVcFromModel(): { [key: string]: any } { 129 | return {}; 130 | } 131 | 132 | /** 133 | * Return all id tokens from model 134 | */ 135 | public getIdTokensFromModel(): { [key: string]: any } { 136 | return {}; 137 | } 138 | } -------------------------------------------------------------------------------- /tests/models/RequestOneVcResponseMissingId.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your presentation exchange request 3 | */ 4 | 5 | import ITestModel from './ITestModel'; 6 | import RequestOneVcResponseOk from './RequestOneVcResponseOk'; 7 | 8 | 9 | export default class RequestOneVcResponseMissingId extends RequestOneVcResponseOk implements ITestModel { 10 | 11 | public preSiopResponseOperations = [ 12 | { 13 | path: '$.presentation_submission.descriptor_map[0].id', 14 | operation: () => undefined 15 | }]; 16 | 17 | } -------------------------------------------------------------------------------- /tests/models/RequestOneVcResponseOk.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your presentation exchange request 3 | */ 4 | 5 | import ITestModel from "./ITestModel"; 6 | import { ClaimToken, TokenType } from "../../lib"; 7 | 8 | 9 | export default class RequestOneVcResponseOk implements ITestModel { 10 | public clientId = 'https://requestor.example.com'; 11 | 12 | /** 13 | * Define the model for the request 14 | */ 15 | public request: any = { 16 | clientId: this.clientId, 17 | clientName: 'My relying party', 18 | clientPurpose: 'Need your VC to provide access', 19 | redirectUri: this.clientId, 20 | tosUri: 'https://tosUri.example.com', 21 | logoUri: 'https://logoUri.example.com', 22 | 23 | presentationDefinition: { 24 | name: 'Get identity card', 25 | purpose: 'Needed to provide you access to the site', 26 | input_descriptors: [ 27 | { 28 | id: 'IdentityCard', 29 | schema: { 30 | uri: ['https://schema.org/IdentityCardCredential'], 31 | name: 'IdentityCard', 32 | purpose: 'Testing the site' 33 | }, 34 | issuance: [ 35 | { 36 | manifest: 'https://portableidentitycards.azure-api.net/dev-v1.0/536279f6-15cc-45f2-be2d-61e352b51eef/portableIdentities/contracts/IdentityCard1' 37 | } 38 | ] 39 | } 40 | ] 41 | } 42 | } 43 | 44 | 45 | /** 46 | * Define the model for the response 47 | */ 48 | public response: any = { 49 | iss: 'https://self-issued.me', 50 | aud: this.clientId, 51 | nonce: '', 52 | state: '', 53 | did: '', 54 | jti: 'fa8fdc8f-d95b-4237-9c90-9696112f4e19', 55 | presentation_submission: { 56 | descriptor_map: [ 57 | { 58 | id: 'IdentityCard', 59 | format: 'jwt', 60 | encoding: 'base64Url', 61 | path: '$.presentation_submission.attestations.presentations.IdentityCard' 62 | } 63 | ], 64 | attestations: { 65 | presentations: { 66 | IdentityCard: { 67 | 'jti': `urn:pic:1`, 68 | 'vc': { 69 | '\@context': [ 70 | 'https://www.w3.org/2018/credentials/v1', 71 | 'https://portableidentitycards.azure-api.net/42b39d9d-0cdd-4ae0-b251-b7b39a561f91/api/portable/v1.0/contracts/test/schema' 72 | ], 73 | 'type': [ 74 | 'VerifiableCredential', 75 | 'IdentityCard' 76 | ], 77 | 'credentialSubject': { 78 | givenName: 'Jules', 79 | familyName: 'Winnfield', 80 | profession: 'hitman' 81 | }, 82 | 'credentialStatus': { 83 | 'id': `https://status.example.com`, 84 | 'type': 'PortableIdentityCardServiceCredentialStatus2020' 85 | } 86 | }, 87 | }, 88 | }, 89 | } 90 | 91 | }, 92 | } 93 | 94 | public responseStatus = { 95 | 'IdentityCard': { 96 | 'aud': 'did:ion:EiBcPY...', 97 | 'credentialStatus': { 98 | 'status': 'valid', 99 | 'reason': `I don't like them` 100 | } 101 | } 102 | }; 103 | 104 | /** 105 | * Return a specific VC 106 | * @param key Name for the vc 107 | */ 108 | public getVcFromResponse(key: string): ClaimToken { 109 | // Decode de presentation 110 | let claimToken = ClaimToken.create(this.response.presentation_submission.attestations.presentations[key]); 111 | 112 | claimToken = ClaimToken.create(claimToken.decodedToken.vp.verifiableCredential[0]); 113 | return claimToken; 114 | } 115 | 116 | 117 | /** 118 | * Return all presentations 119 | */ 120 | public getPresentationsFromModel(): { [key: string]: any } { 121 | return this.response.presentation_submission.attestations.presentations; 122 | } 123 | 124 | /** 125 | * Return all non presented VCs 126 | */ 127 | public getNonPresentedVcFromModel(): { [key: string]: any } { 128 | return {}; 129 | } 130 | 131 | /** 132 | * Return all id tokens from model 133 | */ 134 | public getIdTokensFromModel(): { [key: string]: any } { 135 | return {}; 136 | } 137 | } -------------------------------------------------------------------------------- /tests/models/RequestTwoVcPointerToMultipleTokens.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your presentation exchange request 3 | */ 4 | 5 | import ITestModel from "./ITestModel"; 6 | import RequestTwoVcResponseOk from "./RequestTwoVcResponseOk"; 7 | 8 | 9 | export default class RequestTwoVcPointerToMultipleTokens extends RequestTwoVcResponseOk implements ITestModel { 10 | public preSiopResponseOperations = [ 11 | { 12 | path: '$.presentation_submission.descriptor_map[0].path', 13 | operation: () => '$.presentation_submission.attestations.presentations.*' 14 | }]; 15 | } -------------------------------------------------------------------------------- /tests/models/RequestTwoVcResponseOne.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your presentation exchange request 3 | */ 4 | 5 | import { ClaimToken, TokenType } from '../../lib'; 6 | import RequestTwoVcResponseOk from './RequestTwoVcResponseOk'; 7 | import ITestModel from './ITestModel'; 8 | 9 | 10 | export default class RequestTwoVcResponseOne extends RequestTwoVcResponseOk implements ITestModel { 11 | 12 | public preSiopResponseOperations = [ 13 | { 14 | path: '$.presentation_submission.attestations.presentations.Diploma', 15 | operation: () => undefined 16 | }, 17 | { 18 | path: '$.presentation_submission.descriptor_map[1]', 19 | operation: () => undefined 20 | }]; 21 | } -------------------------------------------------------------------------------- /tests/models/RequestTwoVcResponseRevoked.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class to model your presentation exchange request 3 | */ 4 | 5 | import { ClaimToken, TokenType } from '../../lib'; 6 | import RequestTwoVcResponseOk from './RequestTwoVcResponseOk'; 7 | import ITestModel from './ITestModel'; 8 | 9 | 10 | export default class RequestTwoVcResponseRevoked extends RequestTwoVcResponseOk implements ITestModel { 11 | 12 | 13 | public responseStatus = { 14 | 'IdentityCard': { 15 | 'credentialStatus': { 16 | 'status': 'revoked', 17 | 'reason': `stolen` 18 | } 19 | }, 20 | 'Diploma': { 21 | 'credentialStatus': { 22 | 'status': 'valid', 23 | 'reason': `Accredited` 24 | } 25 | } 26 | }; 27 | } -------------------------------------------------------------------------------- /tests/tools/SiopRequest.spec.ts: -------------------------------------------------------------------------------- 1 | describe('tools', () => { 2 | it ('should validate siop', async () => { 3 | const siop = '' 4 | }); 5 | }); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | 4 | /* Basic Options */ 5 | "target": "es2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ 6 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 7 | "lib": ["es2018", "dom"], /* Specify library files to be included in the compilation. */ 8 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 9 | "sourceMap": true, /* Generates corresponding '.map' file. */ 10 | "outDir": "./dist", /* Redirect output structure to the directory. */ 11 | 12 | /* Strict Type-Checking Options */ 13 | "strict": true, /* Enable all strict type-checking options. */ 14 | "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 15 | 16 | /* Additional Checks */ 17 | "noUnusedLocals": false, /* Report errors on unused locals. */ 18 | "noUnusedParameters": true, /* Report errors on unused parameters. */ 19 | "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 20 | "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 21 | "allowSyntheticDefaultImports": true, 22 | 23 | /* Module Resolution Options */ 24 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 25 | "esModuleInterop": false /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 26 | }, 27 | 28 | "include": [ 29 | "./src/**/*.ts", 30 | "./lib/**/*.ts", 31 | "./tests/**/*.ts" 32 | ], 33 | "exclude": [ 34 | "./node_modules/*" 35 | ] 36 | } --------------------------------------------------------------------------------