Skip to content

Commit

Permalink
feat(core): add W3cCredentialsApi
Browse files Browse the repository at this point in the history
Add an API class to the `W3cCredentialsModule` that allows for CRUD operations for W3C credentials.
  • Loading branch information
karimStekelenburg authored Apr 4, 2023
1 parent c46a6b8 commit c888736
Show file tree
Hide file tree
Showing 17 changed files with 222 additions and 75 deletions.
4 changes: 2 additions & 2 deletions packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
} from '@aries-framework/core'

import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuiteRegistry'
import { W3cVcModuleConfig } from '../../core/src/modules/vc/W3cVcModuleConfig'
import { W3cCredentialsModuleConfig } from '../../core/src/modules/vc/W3cCredentialsModuleConfig'
import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader'
import { getAgentConfig, getAgentContext } from '../../core/tests/helpers'
import { IndySdkWallet } from '../../indy-sdk/src'
Expand Down Expand Up @@ -77,7 +77,7 @@ describeSkipNode17And18('BBS W3cCredentialService', () => {
w3cCredentialService = new W3cCredentialService(
{} as unknown as W3cCredentialRepository,
signatureSuiteRegistry,
new W3cVcModuleConfig({
new W3cCredentialsModuleConfig({
documentLoader: customDocumentLoader,
})
)
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/agent/AgentModules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { GenericRecordsModule } from '../modules/generic-records'
import { MessagePickupModule } from '../modules/message-pìckup'
import { OutOfBandModule } from '../modules/oob'
import { ProofsModule } from '../modules/proofs'
import { MediatorModule, MediationRecipientModule } from '../modules/routing'
import { W3cVcModule } from '../modules/vc'
import { MediationRecipientModule, MediatorModule } from '../modules/routing'
import { W3cCredentialsModule } from '../modules/vc'
import { WalletModule } from '../wallet'

/**
Expand Down Expand Up @@ -129,7 +129,7 @@ function getDefaultAgentModules() {
dids: () => new DidsModule(),
wallet: () => new WalletModule(),
oob: () => new OutOfBandModule(),
w3cVc: () => new W3cVcModule(),
w3cCredentials: () => new W3cCredentialsModule(),
cache: () => new CacheModule(),
} as const
}
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/agent/BaseAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { MessagePickupApi } from '../modules/message-pìckup/MessagePickupApi'
import { OutOfBandApi } from '../modules/oob'
import { ProofsApi } from '../modules/proofs'
import { MediatorApi, MediationRecipientApi } from '../modules/routing'
import { W3cCredentialsApi } from '../modules/vc/W3cCredentialsApi'
import { StorageUpdateService } from '../storage'
import { UpdateAssistant } from '../storage/migration/UpdateAssistant'
import { DEFAULT_UPDATE_CONFIG } from '../storage/migration/updates'
Expand Down Expand Up @@ -56,6 +57,7 @@ export abstract class BaseAgent<AgentModules extends ModulesMap = EmptyModuleMap
public readonly dids: DidsApi
public readonly wallet: WalletApi
public readonly oob: OutOfBandApi
public readonly w3cCredentials: W3cCredentialsApi

public readonly modules: AgentApi<WithoutDefaultModules<AgentModules>>

Expand Down Expand Up @@ -103,6 +105,7 @@ export abstract class BaseAgent<AgentModules extends ModulesMap = EmptyModuleMap
this.dids = this.dependencyManager.resolve(DidsApi)
this.wallet = this.dependencyManager.resolve(WalletApi)
this.oob = this.dependencyManager.resolve(OutOfBandApi)
this.w3cCredentials = this.dependencyManager.resolve(W3cCredentialsApi)

const defaultApis = [
this.connections,
Expand All @@ -117,6 +120,7 @@ export abstract class BaseAgent<AgentModules extends ModulesMap = EmptyModuleMap
this.dids,
this.wallet,
this.oob,
this.w3cCredentials,
]

// Set the api of the registered modules on the agent, excluding the default apis
Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/agent/__tests__/AgentModules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { GenericRecordsModule } from '../../modules/generic-records'
import { MessagePickupModule } from '../../modules/message-pìckup'
import { OutOfBandModule } from '../../modules/oob'
import { ProofsModule } from '../../modules/proofs'
import { MediatorModule, MediationRecipientModule } from '../../modules/routing'
import { W3cVcModule } from '../../modules/vc'
import { MediationRecipientModule, MediatorModule } from '../../modules/routing'
import { W3cCredentialsModule } from '../../modules/vc'
import { DependencyManager, injectable } from '../../plugins'
import { WalletModule } from '../../wallet'
import { extendModulesWithDefaultModules, getAgentApi } from '../AgentModules'
Expand Down Expand Up @@ -67,7 +67,7 @@ describe('AgentModules', () => {
dids: expect.any(DidsModule),
wallet: expect.any(WalletModule),
oob: expect.any(OutOfBandModule),
w3cVc: expect.any(W3cVcModule),
w3cCredentials: expect.any(W3cCredentialsModule),
cache: expect.any(CacheModule),
})
})
Expand All @@ -91,7 +91,7 @@ describe('AgentModules', () => {
dids: expect.any(DidsModule),
wallet: expect.any(WalletModule),
oob: expect.any(OutOfBandModule),
w3cVc: expect.any(W3cVcModule),
w3cCredentials: expect.any(W3cCredentialsModule),
cache: expect.any(CacheModule),
myModule,
})
Expand All @@ -118,7 +118,7 @@ describe('AgentModules', () => {
dids: expect.any(DidsModule),
wallet: expect.any(WalletModule),
oob: expect.any(OutOfBandModule),
w3cVc: expect.any(W3cVcModule),
w3cCredentials: expect.any(W3cCredentialsModule),
cache: expect.any(CacheModule),
myModule,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { JsonTransformer } from '../../../../../utils/JsonTransformer'
import { CacheModule, InMemoryLruCache } from '../../../../cache'
import { DidsModule, KeyDidRegistrar, KeyDidResolver } from '../../../../dids'
import { ProofEventTypes, ProofsModule, V2ProofProtocol } from '../../../../proofs'
import { W3cVcModule } from '../../../../vc'
import { W3cCredentialsModule } from '../../../../vc'
import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader'
import { CredentialEventTypes } from '../../../CredentialEvents'
import { CredentialsModule } from '../../../CredentialsModule'
Expand Down Expand Up @@ -126,7 +126,7 @@ const getIndyJsonLdModules = () =>
cache: new CacheModule({
cache: new InMemoryLruCache({ limit: 100 }),
}),
w3cVc: new W3cVcModule({
w3cVc: new W3cCredentialsModule({
documentLoader: customDocumentLoader,
}),
} as const)
Expand Down
24 changes: 12 additions & 12 deletions packages/core/src/modules/vc/W3cCredentialService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { VerificationMethod } from '../dids'
import { getKeyFromVerificationMethod } from '../dids/domain/key-type'

import { SignatureSuiteRegistry } from './SignatureSuiteRegistry'
import { W3cVcModuleConfig } from './W3cVcModuleConfig'
import { W3cCredentialsModuleConfig } from './W3cCredentialsModuleConfig'
import { deriveProof } from './deriveProof'
import { orArrayToArray, w3cDate } from './jsonldUtil'
import jsonld from './libraries/jsonld'
Expand All @@ -35,16 +35,16 @@ import { W3cCredentialRecord, W3cCredentialRepository } from './repository'
export class W3cCredentialService {
private w3cCredentialRepository: W3cCredentialRepository
private signatureSuiteRegistry: SignatureSuiteRegistry
private w3cVcModuleConfig: W3cVcModuleConfig
private w3cCredentialsModuleConfig: W3cCredentialsModuleConfig

public constructor(
w3cCredentialRepository: W3cCredentialRepository,
signatureSuiteRegistry: SignatureSuiteRegistry,
w3cVcModuleConfig: W3cVcModuleConfig
w3cCredentialsModuleConfig: W3cCredentialsModuleConfig
) {
this.w3cCredentialRepository = w3cCredentialRepository
this.signatureSuiteRegistry = signatureSuiteRegistry
this.w3cVcModuleConfig = w3cVcModuleConfig
this.w3cCredentialsModuleConfig = w3cCredentialsModuleConfig
}

/**
Expand Down Expand Up @@ -89,7 +89,7 @@ export class W3cCredentialService {
credential: JsonTransformer.toJSON(options.credential),
suite: suite,
purpose: options.proofPurpose,
documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext),
documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext),
})

return JsonTransformer.fromJSON(result, W3cVerifiableCredential)
Expand All @@ -112,7 +112,7 @@ export class W3cCredentialService {
const verifyOptions: Record<string, unknown> = {
credential: JsonTransformer.toJSON(options.credential),
suite: suites,
documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext),
documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext),
checkStatus: () => {
if (verifyRevocationState) {
throw new AriesFrameworkError('Revocation for W3C credentials is currently not supported')
Expand Down Expand Up @@ -182,7 +182,7 @@ export class W3cCredentialService {
throw new AriesFrameworkError('The key type of the verification method does not match the suite')
}

const documentLoader = this.w3cVcModuleConfig.documentLoader(agentContext)
const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext)
const verificationMethodObject = (await documentLoader(options.verificationMethod)).document as Record<
string,
unknown
Expand All @@ -209,7 +209,7 @@ export class W3cCredentialService {
presentation: JsonTransformer.toJSON(options.presentation),
suite: suite,
challenge: options.challenge,
documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext),
documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext),
})

return JsonTransformer.fromJSON(result, W3cVerifiablePresentation)
Expand Down Expand Up @@ -262,7 +262,7 @@ export class W3cCredentialService {
presentation: JsonTransformer.toJSON(options.presentation),
suite: allSuites,
challenge: options.challenge,
documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext),
documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext),
}

// this is a hack because vcjs throws if purpose is passed as undefined or null
Expand All @@ -284,7 +284,7 @@ export class W3cCredentialService {

const proof = await deriveProof(JsonTransformer.toJSON(options.credential), options.revealDocument, {
suite: suite,
documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext),
documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext),
})

return proof
Expand All @@ -294,7 +294,7 @@ export class W3cCredentialService {
agentContext: AgentContext,
verificationMethod: string
): Promise<Key> {
const documentLoader = this.w3cVcModuleConfig.documentLoader(agentContext)
const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext)
const verificationMethodObject = await documentLoader(verificationMethod)
const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod)

Expand All @@ -315,7 +315,7 @@ export class W3cCredentialService {
// Get the expanded types
const expandedTypes = (
await jsonld.expand(JsonTransformer.toJSON(options.credential), {
documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext),
documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext),
})
)[0]['@type']

Expand Down
42 changes: 42 additions & 0 deletions packages/core/src/modules/vc/W3cCredentialsApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { W3cVerifiableCredential, StoreCredentialOptions } from './models'
import type { W3cCredentialRecord } from './repository'
import type { Query } from '../../storage/StorageService'

import { AgentContext } from '../../agent'
import { injectable } from '../../plugins'

import { W3cCredentialService } from './W3cCredentialService'

/**
* @public
*/
@injectable()
export class W3cCredentialsApi {
private agentContext: AgentContext
private w3cCredentialService: W3cCredentialService

public constructor(agentContext: AgentContext, w3cCredentialService: W3cCredentialService) {
this.agentContext = agentContext
this.w3cCredentialService = w3cCredentialService
}

public async storeCredential(options: StoreCredentialOptions): Promise<W3cCredentialRecord> {
return this.w3cCredentialService.storeCredential(this.agentContext, options)
}

public async removeCredentialRecord(id: string) {
return this.w3cCredentialService.removeCredentialRecord(this.agentContext, id)
}

public async getAllCredentialRecords(): Promise<W3cCredentialRecord[]> {
return this.w3cCredentialService.getAllCredentialRecords(this.agentContext)
}

public async getCredentialRecordById(id: string): Promise<W3cCredentialRecord> {
return this.w3cCredentialService.getCredentialRecordById(this.agentContext, id)
}

public async findCredentialRecordsByQuery(query: Query<W3cCredentialRecord>): Promise<W3cVerifiableCredential[]> {
return this.w3cCredentialService.findCredentialsByQuery(this.agentContext, query)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { W3cVcModuleConfigOptions } from './W3cVcModuleConfig'
import type { W3cVcModuleConfigOptions } from './W3cCredentialsModuleConfig'
import type { DependencyManager, Module } from '../../plugins'

import { KeyType } from '../../crypto'
Expand All @@ -9,25 +9,31 @@ import {

import { SignatureSuiteRegistry, SignatureSuiteToken } from './SignatureSuiteRegistry'
import { W3cCredentialService } from './W3cCredentialService'
import { W3cVcModuleConfig } from './W3cVcModuleConfig'
import { W3cCredentialsApi } from './W3cCredentialsApi'
import { W3cCredentialsModuleConfig } from './W3cCredentialsModuleConfig'
import { W3cCredentialRepository } from './repository/W3cCredentialRepository'
import { Ed25519Signature2018 } from './signature-suites'

export class W3cVcModule implements Module {
public readonly config: W3cVcModuleConfig
/**
* @public
*/
export class W3cCredentialsModule implements Module {
public readonly config: W3cCredentialsModuleConfig
public readonly api = W3cCredentialsApi

public constructor(config?: W3cVcModuleConfigOptions) {
this.config = new W3cVcModuleConfig(config)
this.config = new W3cCredentialsModuleConfig(config)
}

public register(dependencyManager: DependencyManager) {
dependencyManager.registerContextScoped(W3cCredentialsApi)
dependencyManager.registerSingleton(W3cCredentialService)
dependencyManager.registerSingleton(W3cCredentialRepository)

dependencyManager.registerSingleton(SignatureSuiteRegistry)

// Register the config
dependencyManager.registerInstance(W3cVcModuleConfig, this.config)
dependencyManager.registerInstance(W3cCredentialsModuleConfig, this.config)

// Always register ed25519 signature suite
dependencyManager.registerInstance(SignatureSuiteToken, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export interface W3cVcModuleConfigOptions {
documentLoader?: DocumentLoaderWithContext
}

export class W3cVcModuleConfig {
export class W3cCredentialsModuleConfig {
private options: W3cVcModuleConfigOptions

public constructor(options?: W3cVcModuleConfigOptions) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { KeyType } from '../../../crypto'
import { DependencyManager } from '../../../plugins/DependencyManager'
import { SignatureSuiteRegistry, SignatureSuiteToken } from '../SignatureSuiteRegistry'
import { W3cCredentialService } from '../W3cCredentialService'
import { W3cVcModule } from '../W3cVcModule'
import { W3cVcModuleConfig } from '../W3cVcModuleConfig'
import { W3cCredentialsApi } from '../W3cCredentialsApi'
import { W3cCredentialsModule } from '../W3cCredentialsModule'
import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig'
import { W3cCredentialRepository } from '../repository'
import { Ed25519Signature2018 } from '../signature-suites'

Expand All @@ -12,19 +13,20 @@ const DependencyManagerMock = DependencyManager as jest.Mock<DependencyManager>

const dependencyManager = new DependencyManagerMock()

describe('W3cVcModule', () => {
describe('W3cCredentialsModule', () => {
test('registers dependencies on the dependency manager', () => {
const module = new W3cVcModule()
const module = new W3cCredentialsModule()

module.register(dependencyManager)

expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3)
expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(W3cCredentialsApi)
expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialService)
expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialRepository)
expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SignatureSuiteRegistry)

expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(2)
expect(dependencyManager.registerInstance).toHaveBeenCalledWith(W3cVcModuleConfig, module.config)
expect(dependencyManager.registerInstance).toHaveBeenCalledWith(W3cCredentialsModuleConfig, module.config)

expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, {
suiteClass: Ed25519Signature2018,
Expand Down
Loading

0 comments on commit c888736

Please sign in to comment.