Skip to content

Commit

Permalink
fix(credential-ld): include credential context and fix context loader…
Browse files Browse the repository at this point in the history
… Map
  • Loading branch information
mirceanis committed Nov 24, 2021
1 parent 3e2cf29 commit ef7797d
Show file tree
Hide file tree
Showing 7 changed files with 403 additions and 11 deletions.
237 changes: 237 additions & 0 deletions packages/credential-ld/contexts/w3_2018_credentials_v1.jsonld
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
{
"@context": {
"@version": 1.1,
"@protected": true,

"id": "@id",
"type": "@type",

"VerifiableCredential": {
"@id": "https://www.w3.org/2018/credentials#VerifiableCredential",
"@context": {
"@version": 1.1,
"@protected": true,

"id": "@id",
"type": "@type",

"cred": "https://www.w3.org/2018/credentials#",
"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",

"credentialSchema": {
"@id": "cred:credentialSchema",
"@type": "@id",
"@context": {
"@version": 1.1,
"@protected": true,

"id": "@id",
"type": "@type",

"cred": "https://www.w3.org/2018/credentials#",

"JsonSchemaValidator2018": "cred:JsonSchemaValidator2018"
}
},
"credentialStatus": {"@id": "cred:credentialStatus", "@type": "@id"},
"credentialSubject": {"@id": "cred:credentialSubject", "@type": "@id"},
"evidence": {"@id": "cred:evidence", "@type": "@id"},
"expirationDate": {"@id": "cred:expirationDate", "@type": "xsd:dateTime"},
"holder": {"@id": "cred:holder", "@type": "@id"},
"issued": {"@id": "cred:issued", "@type": "xsd:dateTime"},
"issuer": {"@id": "cred:issuer", "@type": "@id"},
"issuanceDate": {"@id": "cred:issuanceDate", "@type": "xsd:dateTime"},
"proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"},
"refreshService": {
"@id": "cred:refreshService",
"@type": "@id",
"@context": {
"@version": 1.1,
"@protected": true,

"id": "@id",
"type": "@type",

"cred": "https://www.w3.org/2018/credentials#",

"ManualRefreshService2018": "cred:ManualRefreshService2018"
}
},
"termsOfUse": {"@id": "cred:termsOfUse", "@type": "@id"},
"validFrom": {"@id": "cred:validFrom", "@type": "xsd:dateTime"},
"validUntil": {"@id": "cred:validUntil", "@type": "xsd:dateTime"}
}
},

"VerifiablePresentation": {
"@id": "https://www.w3.org/2018/credentials#VerifiablePresentation",
"@context": {
"@version": 1.1,
"@protected": true,

"id": "@id",
"type": "@type",

"cred": "https://www.w3.org/2018/credentials#",
"sec": "https://w3id.org/security#",

"holder": {"@id": "cred:holder", "@type": "@id"},
"proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"},
"verifiableCredential": {"@id": "cred:verifiableCredential", "@type": "@id", "@container": "@graph"}
}
},

"EcdsaSecp256k1Signature2019": {
"@id": "https://w3id.org/security#EcdsaSecp256k1Signature2019",
"@context": {
"@version": 1.1,
"@protected": true,

"id": "@id",
"type": "@type",

"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",

"challenge": "sec:challenge",
"created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"},
"domain": "sec:domain",
"expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
"jws": "sec:jws",
"nonce": "sec:nonce",
"proofPurpose": {
"@id": "sec:proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,

"id": "@id",
"type": "@type",

"sec": "https://w3id.org/security#",

"assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"},
"authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"}
}
},
"proofValue": "sec:proofValue",
"verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"}
}
},

"EcdsaSecp256r1Signature2019": {
"@id": "https://w3id.org/security#EcdsaSecp256r1Signature2019",
"@context": {
"@version": 1.1,
"@protected": true,

"id": "@id",
"type": "@type",

"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",

"challenge": "sec:challenge",
"created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"},
"domain": "sec:domain",
"expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
"jws": "sec:jws",
"nonce": "sec:nonce",
"proofPurpose": {
"@id": "sec:proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,

"id": "@id",
"type": "@type",

"sec": "https://w3id.org/security#",

"assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"},
"authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"}
}
},
"proofValue": "sec:proofValue",
"verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"}
}
},

"Ed25519Signature2018": {
"@id": "https://w3id.org/security#Ed25519Signature2018",
"@context": {
"@version": 1.1,
"@protected": true,

"id": "@id",
"type": "@type",

"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",

"challenge": "sec:challenge",
"created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"},
"domain": "sec:domain",
"expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
"jws": "sec:jws",
"nonce": "sec:nonce",
"proofPurpose": {
"@id": "sec:proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,

"id": "@id",
"type": "@type",

"sec": "https://w3id.org/security#",

"assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"},
"authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"}
}
},
"proofValue": "sec:proofValue",
"verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"}
}
},

"RsaSignature2018": {
"@id": "https://w3id.org/security#RsaSignature2018",
"@context": {
"@version": 1.1,
"@protected": true,

"challenge": "sec:challenge",
"created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"},
"domain": "sec:domain",
"expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
"jws": "sec:jws",
"nonce": "sec:nonce",
"proofPurpose": {
"@id": "sec:proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,

"id": "@id",
"type": "@type",

"sec": "https://w3id.org/security#",

"assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"},
"authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"}
}
},
"proofValue": "sec:proofValue",
"verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"}
}
},

"proof": {"@id": "https://w3id.org/security#proof", "@type": "@id", "@container": "@graph"}
}
}
39 changes: 39 additions & 0 deletions packages/credential-ld/src/__tests__/context.loader.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ContextDoc } from '../types'
import { LdContextLoader } from '../ld-context-loader'
import { LdDefaultContexts } from '../ld-default-contexts'

describe('credential-ld context loader', () => {
const customContext: Record<string, ContextDoc> = {
'https://example.com/custom/context': {
'@context': {
'@version': 1.1,
id: '@id',
type: '@type',
nothing: 'https://example.com/nothing',
},
},
}

it('loads custom context from record', async () => {
expect.assertions(2)
const loader = new LdContextLoader({ contextsPaths: [customContext] })
expect(loader.has('https://example.com/custom/context')).toBe(true)
await expect(loader.get('https://example.com/custom/context')).resolves.toEqual({
'@context': {
'@version': 1.1,
id: '@id',
type: '@type',
nothing: 'https://example.com/nothing',
},
})
})

it('loads context from default map', async () => {
expect.assertions(2)
const loader = new LdContextLoader({ contextsPaths: [LdDefaultContexts] })
expect(loader.has('https://www.w3.org/2018/credentials/v1')).toBe(true)

const credsContext = await loader.get('https://www.w3.org/2018/credentials/v1')
expect(credsContext['@context']).toBeDefined()
})
})
84 changes: 84 additions & 0 deletions packages/credential-ld/src/__tests__/issue-verify-flow.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
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 { ContextDoc } from '../types'
import { CredentialIssuerLD } from '../action-handler'
import { LdDefaultContexts } from '../ld-default-contexts'
import { VeramoEd25519Signature2018 } from '../suites/Ed25519Signature2018'
import { Resolver } from 'did-resolver'

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

describe('credential-LD full flow', () => {
let didKeyIdentifier: 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' }),
},
store: new MemoryDIDStore(),
defaultProvider: 'did:key',
}),
new DIDResolverPlugin({
resolver: new Resolver({ ...getDidKeyResolver() }),
}),
new CredentialIssuer(),
new CredentialIssuerLD({
contextMaps: [LdDefaultContexts, customContext],
suites: [new VeramoEd25519Signature2018()],
}),
],
})
didKeyIdentifier = await agent.didManagerCreate()
})

it('works with Ed25519Signature2018', async () => {
const credential: CredentialPayload = {
issuer: didKeyIdentifier.did,
'@context': ['custom:example.context'],
credentialSubject: {
nothing: 'else matters',
},
}
const verifiableCredential = await agent.createVerifiableCredential({
credential,
proofFormat: 'lds',
})

expect(verifiableCredential).toBeDefined()

const verified = await agent.verifyCredential({
credential: verifiableCredential,
})

expect(verified).toBe(true)
})
})
13 changes: 7 additions & 6 deletions packages/credential-ld/src/ld-context-loader.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
/**
* The LdContextLoader is initialized with a List of Map<string, ContextDoc>
* The LdContextLoader is initialized with a List of Map<string, ContextDoc | Promise<ContextDoc>>
* that it unifies into a single Map to provide to the documentLoader within
* the w3c credential module.
*/
import { OrPromise, RecordLike } from '@veramo/utils'
import { isIterable, OrPromise, RecordLike } from '@veramo/utils'
import { ContextDoc } from './types'

export class LdContextLoader {
private contexts: Record<string, OrPromise<ContextDoc>>
private readonly contexts: Record<string, OrPromise<ContextDoc>>

constructor(options: { contextsPaths: RecordLike<OrPromise<ContextDoc>>[] }) {
this.contexts = {}
// generate-plugin-schema is failing unless we use the cast to `any[]`
Array.from(options.contextsPaths as any[], (mapItem) => {
for (const [key, value] of mapItem) {
Array.from(options.contextsPaths, (mapItem) => {
const map = isIterable(mapItem) ? mapItem : Object.entries(mapItem)
// generate-plugin-schema is failing unless we use the cast to `any[]`
for (const [key, value] of map as any[]) {
this.contexts[key] = value
}
})
Expand Down
Loading

0 comments on commit ef7797d

Please sign in to comment.