Skip to content

Commit

Permalink
feat(credential-w3c): add override policies to verifyPresentation (#990)
Browse files Browse the repository at this point in the history
relates to #375
relates to #954
  • Loading branch information
daniyal-khalil committed Aug 18, 2022
1 parent 9bed70b commit 06b3147
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 7 deletions.
2 changes: 1 addition & 1 deletion packages/cli/src/presentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ presentation
challenge: options.challenge,
domain: options.domain,
})
if (result === true) {
if (result.verified === true) {
console.log('Presentation was verified successfully.')
} else {
console.error('Presentation could not be verified.')
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export * from './types/IKeyManager'
export * from './types/IMessage'
export * from './types/IMessageHandler'
export * from './types/IResolver'
export * from './types/IError'
export * from './types/IVerifyResult'
export * from './types/vc-data-model'

/**
Expand Down
16 changes: 16 additions & 0 deletions packages/core/src/types/IError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* An error object, which can contain a code.
* @beta
*/
export interface IError {

/**
* The details of the error being throw or forwarded
*/
message?: string

/**
* The code for the error being throw
*/
errorCode?: string
}
25 changes: 25 additions & 0 deletions packages/core/src/types/IVerifyResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { IError } from "./IError"
/**
* Encapsulates the response object to verifyPresentation method after verifying a
* {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation}
*
* @beta
*/
export interface IVerifyResult {
/**
* This value is used to transmit the result of verification.
*/
verified: boolean

/**
* Optional Error object for the
* but currently the machine readable errors are not expored from DID-JWT package to be imported here
*/
error?: IError

/**
* Other options can be specified for verification.
* They will be forwarded to the lower level modules. that performt the checks
*/
[x: string]: any
}
65 changes: 64 additions & 1 deletion packages/credential-w3c/plugin.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,10 @@
"type": "boolean",
"description": "When dealing with JSON-LD you also MUST provide the proper contexts. Set this to `true` ONLY if you want the '@context' URLs to be fetched in case they are not pre-loaded. The context definitions SHOULD rather be provided at startup instead of being fetched.",
"default": false
},
"policies": {
"$ref": "#/components/schemas/VerifyPresentationPolicies",
"description": "Verification Policies for the verifiable presentation These will also be forwarded to the lower level module"
}
},
"required": [
Expand All @@ -421,6 +425,65 @@
}
],
"description": "Represents a signed Verifiable Presentation (includes proof) in either JSON or compact JWT format. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }"
},
"VerifyPresentationPolicies": {
"type": "object",
"properties": {
"now": {
"type": "number",
"description": "policy to over the now (current time) during the verification check"
},
"issuanceDate": {
"type": "boolean",
"description": "policy to override the issuanceDate (nbf) timestamp check"
},
"issuedAtDate": {
"type": "boolean",
"description": "policy to override the issuedAtDate (iat) timestamp check"
},
"expirationDate": {
"type": "boolean",
"description": "policy to override the expirationDate (exp) timestamp check"
}
},
"additionalProperties": {
"description": "Other options can be specified for verification. They will be forwarded to the lower level modules that perform the checks"
},
"description": "These optional settings can be used to override some of the default checks that are performed on Presentations during verification."
},
"IVerifyResult": {
"type": "object",
"properties": {
"verified": {
"type": "boolean",
"description": "This value is used to transmit the result of verification."
},
"error": {
"$ref": "#/components/schemas/IError",
"description": "Optional Error object for the but currently the machine readable errors are not expored from DID-JWT package to be imported here"
}
},
"required": [
"verified"
],
"additionalProperties": {
"description": "Other options can be specified for verification. They will be forwarded to the lower level modules. that performt the checks"
},
"description": "Encapsulates the response object to verifyPresentation method after verifying a\n {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }"
},
"IError": {
"type": "object",
"properties": {
"message": {
"type": "string",
"description": "The details of the error being throw or forwarded"
},
"errorCode": {
"type": "string",
"description": "The code for the error being throw"
}
},
"description": "An error object, which can contain a code."
}
},
"methods": {
Expand Down Expand Up @@ -457,7 +520,7 @@
"$ref": "#/components/schemas/IVerifyPresentationArgs"
},
"returnType": {
"type": "boolean"
"$ref": "#/components/schemas/IVerifyResult"
}
}
}
Expand Down
157 changes: 157 additions & 0 deletions packages/credential-w3c/src/__tests__/issue-verify-flow.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import {
createAgent,
CredentialPayload,
IDIDManager,
IIdentifier,
IKeyManager,
IResolver,
TAgent,
} from '../../../core/src'
import { CredentialIssuer, ICredentialIssuer } from '../../../credential-w3c/src'
import { DIDManager, MemoryDIDStore } from '../../../did-manager/src'
import { KeyManager, MemoryKeyStore, MemoryPrivateKeyStore } from '../../../key-manager/src'
import { KeyManagementSystem } from '../../../kms-local/src'
import { getDidKeyResolver, KeyDIDProvider } from '../../../did-provider-key/src'
import { DIDResolverPlugin } from '../../../did-resolver/src'
import { EthrDIDProvider } from "../../../did-provider-ethr/src";
import { ContextDoc } from '../../../credential-ld/src/types'
import { Resolver } from 'did-resolver'
import { getResolver as ethrDidResolver } from 'ethr-did-resolver'

jest.setTimeout(300000)

const customContext: Record<string, ContextDoc> = {
'custom:example.context': {
'@context': {
nothing: 'custom:example.context#blank',
},
},
}

const infuraProjectId = '3586660d179141e3801c3895de1c2eba'

describe('credential-w3c full flow', () => {
let didKeyIdentifier: IIdentifier
let didEthrIdentifier: IIdentifier
let agent: TAgent<IResolver & IKeyManager & IDIDManager & ICredentialIssuer>

beforeAll(async () => {
agent = createAgent({
plugins: [
new KeyManager({
store: new MemoryKeyStore(),
kms: {
local: new KeyManagementSystem(new MemoryPrivateKeyStore()),
},
}),
new DIDManager({
providers: {
'did:key': new KeyDIDProvider({ defaultKms: 'local' }),
'did:ethr:goerli': new EthrDIDProvider({
defaultKms: 'local',
network: 'goerli',
}),
},
store: new MemoryDIDStore(),
defaultProvider: 'did:key',
}),
new DIDResolverPlugin({
resolver: new Resolver({
...getDidKeyResolver(),
...ethrDidResolver({ infuraProjectId, }),
}),
}),
new CredentialIssuer(),
],
})
didKeyIdentifier = await agent.didManagerCreate()
didEthrIdentifier = await agent.didManagerCreate({ provider: "did:ethr:goerli" })
})

it('verify a verifiablePresentation', async () => {
const credential: CredentialPayload = {
issuer: didKeyIdentifier.did,
'@context': ['custom:example.context'],
credentialSubject: {
nothing: 'else matters',
},
}
const verifiableCredential1 = await agent.createVerifiableCredential({
credential,
proofFormat: 'jwt',
})

const verifiablePresentation = await agent.createVerifiablePresentation({
presentation: {
verifiableCredential: [verifiableCredential1],
holder: didKeyIdentifier.did
},
challenge: "VERAMO",
proofFormat: 'jwt',
})

expect(verifiablePresentation).toBeDefined()

const response = await agent.verifyPresentation({
presentation: verifiablePresentation,
challenge: "VERAMO",
})

expect(response.verified).toBe(true)
})

it.only('fails the verification of an expired credential', async () => {
const presentationJWT = 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjAyOTcyMTAsInZwIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6WyJleUpoYkdjaU9pSkZaRVJUUVNJc0luUjVjQ0k2SWtwWFZDSjkuZXlKbGVIQWlPakUyTmpBeU9UY3lNVEFzSW5aaklqcDdJa0JqYjI1MFpYaDBJanBiSW1oMGRIQnpPaTh2ZDNkM0xuY3pMbTl5Wnk4eU1ERTRMMk55WldSbGJuUnBZV3h6TDNZeElpd2lZM1Z6ZEc5dE9tVjRZVzF3YkdVdVkyOXVkR1Y0ZENKZExDSjBlWEJsSWpwYklsWmxjbWxtYVdGaWJHVkRjbVZrWlc1MGFXRnNJbDBzSW1OeVpXUmxiblJwWVd4VGRXSnFaV04wSWpwN0ltNXZkR2hwYm1jaU9pSmxiSE5sSUcxaGRIUmxjbk1pZlgwc0ltNWlaaUk2TVRZMk1ESTVOekl4TUN3aWFYTnpJam9pWkdsa09tdGxlVHA2TmsxcmFWVTNVbk5hVnpOeWFXVmxRMjg1U25OMVVEUnpRWEZYZFdGRE0zbGhjbWwxWVZCMlVXcHRZVzVsWTFBaWZRLkZhdzBEUWNNdXpacEVkcy1LR3dOalMyM2IzbUEzZFhQWXBQcGJzNmRVSnhIOVBrZzVieGF3UDVwMlNPajdQM25IdEpCR3lwTjJ3NzRfZjc3SjF5dUJ3Il19LCJuYmYiOjE2NjAyOTcyMTAsImlzcyI6ImRpZDprZXk6ejZNa2lVN1JzWlczcmllZUNvOUpzdVA0c0FxV3VhQzN5YXJpdWFQdlFqbWFuZWNQIn0.YcYbyqVlD8YsTjVw0kCEs0P_ie6SFMakf_ncPntEjsmS9C4cKyiS50ZhNkOv0R3Roy1NrzX7h93WBU55KeJlCw'

const response = await agent.verifyPresentation({
presentation: presentationJWT,
})

expect(response.verified).toBe(false)
expect(response.error).toBeDefined()
expect(response.error?.message).toContain('JWT has expired')
})


it('fails the verification with nbf in the future',async () => {
const presentationJWT = 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2cCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iXSwidmVyaWZpYWJsZUNyZWRlbnRpYWwiOlsiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNklrcFhWQ0o5LmV5SjJZeUk2ZXlKQVkyOXVkR1Y0ZENJNld5Sm9kSFJ3Y3pvdkwzZDNkeTUzTXk1dmNtY3ZNakF4T0M5amNtVmtaVzUwYVdGc2N5OTJNU0lzSW1OMWMzUnZiVHBsZUdGdGNHeGxMbU52Ym5SbGVIUWlYU3dpZEhsd1pTSTZXeUpXWlhKcFptbGhZbXhsUTNKbFpHVnVkR2xoYkNKZExDSmpjbVZrWlc1MGFXRnNVM1ZpYW1WamRDSTZleUp1YjNSb2FXNW5Jam9pWld4elpTQnRZWFIwWlhKekluMTlMQ0p1WW1ZaU9qRXhOall3TWprNE5UZzRMQ0pwYzNNaU9pSmthV1E2YTJWNU9ubzJUV3QyYlhCeFRXbDFOM2h1U25kVE9YQkVSR0ZSYW1oQ1dUWndlbU00V1RKQ2FWRnhSWFUwZW1GRldFMVdUQ0o5LnA4Y2FTS1pTcGdISm1TRzhMekpnSWlWMzFRU3NjOEJ2anZuQ1JrOEM3X1UxLXV5cS11MHlQcDdjRWlSOUtXTnprN2RDQlBiR2pBRGRiNC0tV3V5LUNRIl19LCJuYmYiOjI2NjAyOTg1ODgsImlzcyI6ImRpZDprZXk6ejZNa3ZtcHFNaXU3eG5Kd1M5cEREYVFqaEJZNnB6YzhZMkJpUXFFdTR6YUVYTVZMIiwibm9uY2UiOiJWRVJBTU8ifQ.F-uiI2iVMcdm1VFzkXgtZqq8QGw5XnyEI36vGblBluHnklnNYNmE5eluQ23dbcduGWSe3ZJJ65C7HrPTUoXvDA'

const response = await agent.verifyPresentation({
presentation: presentationJWT,
})

expect(response.verified).toBe(false)
expect(response.error).toBeDefined()
expect(response.error?.message).toContain('JWT not valid before nbf')
})

/**
* These tests can be uncommented out when the did-jwt starts to support the policies merge request
*/

// it('passes the verification of an expired credential with policy exp false',async () => {
// const presentationJWT = 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2cCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iXSwidmVyaWZpYWJsZUNyZWRlbnRpYWwiOlsiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNklrcFhWQ0o5LmV5SjJZeUk2ZXlKQVkyOXVkR1Y0ZENJNld5Sm9kSFJ3Y3pvdkwzZDNkeTUzTXk1dmNtY3ZNakF4T0M5amNtVmtaVzUwYVdGc2N5OTJNU0lzSW1OMWMzUnZiVHBsZUdGdGNHeGxMbU52Ym5SbGVIUWlYU3dpZEhsd1pTSTZXeUpXWlhKcFptbGhZbXhsUTNKbFpHVnVkR2xoYkNKZExDSmpjbVZrWlc1MGFXRnNVM1ZpYW1WamRDSTZleUp1YjNSb2FXNW5Jam9pWld4elpTQnRZWFIwWlhKekluMTlMQ0p1WW1ZaU9qRXhOall3TWprNE5UZzRMQ0pwYzNNaU9pSmthV1E2YTJWNU9ubzJUV3QyYlhCeFRXbDFOM2h1U25kVE9YQkVSR0ZSYW1oQ1dUWndlbU00V1RKQ2FWRnhSWFUwZW1GRldFMVdUQ0o5LnA4Y2FTS1pTcGdISm1TRzhMekpnSWlWMzFRU3NjOEJ2anZuQ1JrOEM3X1UxLXV5cS11MHlQcDdjRWlSOUtXTnprN2RDQlBiR2pBRGRiNC0tV3V5LUNRIl19LCJuYmYiOjI2NjAyOTg1ODgsImlzcyI6ImRpZDprZXk6ejZNa3ZtcHFNaXU3eG5Kd1M5cEREYVFqaEJZNnB6YzhZMkJpUXFFdTR6YUVYTVZMIiwibm9uY2UiOiJWRVJBTU8ifQ.F-uiI2iVMcdm1VFzkXgtZqq8QGw5XnyEI36vGblBluHnklnNYNmE5eluQ23dbcduGWSe3ZJJ65C7HrPTUoXvDA'

// const response = await agent.verifyPresentation({
// presentation: presentationJWT,
// policies: {
// exp: false
// }
// })

// expect(response.verified).toBe(true)
// })

// it('passes the verification with nbf in the future with policy nbf false',async () => {
// const presentationJWT = 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2cCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iXSwidmVyaWZpYWJsZUNyZWRlbnRpYWwiOlsiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNklrcFhWQ0o5LmV5SjJZeUk2ZXlKQVkyOXVkR1Y0ZENJNld5Sm9kSFJ3Y3pvdkwzZDNkeTUzTXk1dmNtY3ZNakF4T0M5amNtVmtaVzUwYVdGc2N5OTJNU0lzSW1OMWMzUnZiVHBsZUdGdGNHeGxMbU52Ym5SbGVIUWlYU3dpZEhsd1pTSTZXeUpXWlhKcFptbGhZbXhsUTNKbFpHVnVkR2xoYkNKZExDSmpjbVZrWlc1MGFXRnNVM1ZpYW1WamRDSTZleUp1YjNSb2FXNW5Jam9pWld4elpTQnRZWFIwWlhKekluMTlMQ0p1WW1ZaU9qRXhOall3TWprNE5UZzRMQ0pwYzNNaU9pSmthV1E2YTJWNU9ubzJUV3QyYlhCeFRXbDFOM2h1U25kVE9YQkVSR0ZSYW1oQ1dUWndlbU00V1RKQ2FWRnhSWFUwZW1GRldFMVdUQ0o5LnA4Y2FTS1pTcGdISm1TRzhMekpnSWlWMzFRU3NjOEJ2anZuQ1JrOEM3X1UxLXV5cS11MHlQcDdjRWlSOUtXTnprN2RDQlBiR2pBRGRiNC0tV3V5LUNRIl19LCJuYmYiOjI2NjAyOTg1ODgsImlzcyI6ImRpZDprZXk6ejZNa3ZtcHFNaXU3eG5Kd1M5cEREYVFqaEJZNnB6YzhZMkJpUXFFdTR6YUVYTVZMIiwibm9uY2UiOiJWRVJBTU8ifQ.F-uiI2iVMcdm1VFzkXgtZqq8QGw5XnyEI36vGblBluHnklnNYNmE5eluQ23dbcduGWSe3ZJJ65C7HrPTUoXvDA'

// const response = await agent.verifyPresentation({
// presentation: presentationJWT,
// policies: {
// nbf: false
// }
// })

// expect(response.verified).toBe(true)
// })
})
Loading

0 comments on commit 06b3147

Please sign in to comment.