Skip to content

Commit

Permalink
feat: crete openid4vc-holder package
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Auer <martin.auer97@gmail.com>
  • Loading branch information
auer-martin committed Oct 10, 2023
1 parent 282258e commit 14c61ff
Show file tree
Hide file tree
Showing 20 changed files with 2,542 additions and 0 deletions.
167 changes: 167 additions & 0 deletions packages/openid4vc-holder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<p align="center">
<br />
<img
alt="Hyperledger Aries logo"
src="https://raw.githubusercontent.com/hyperledger/aries-framework-javascript/aa31131825e3331dc93694bc58414d955dcb1129/images/aries-logo.png"
height="250px"
/>
</p>
<h1 align="center"><b>Aries Framework JavaScript Open ID Connect For Verifiable Credentials Client Module</b></h1>
<p align="center">
<a
href="https://raw.githubusercontent.com/hyperledger/aries-framework-javascript/main/LICENSE"
><img
alt="License"
src="https://img.shields.io/badge/License-Apache%202.0-blue.svg"
/></a>
<a href="https://www.typescriptlang.org/"
><img
alt="typescript"
src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg"
/></a>
<a href="https://www.npmjs.com/package/@aries-framework/openid4vc-holder"
><img
alt="@aries-framework/openid4vc-holder version"
src="https://img.shields.io/npm/v/@aries-framework/openid4vc-holder"
/></a>

</p>
<br />

Open ID Connect For Verifiable Credentials Holder Module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript).

### Installation

Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository.

```sh
yarn add @aries-framework/openid4vc-holder
```

### Quick start

#### Requirements

Before a credential can be requested, you need the issuer URI. This URI starts with `openid-initiate-issuance://` and is provided by the issuer. The issuer URI is commonly acquired by scanning a QR code.

#### Module registration

In order to get this module to work, we need to inject it into the agent. This makes the module's functionality accessible through the agent's `modules` api.

```ts
import { OpenId4VcHolderModule } from '@aries-framework/openid4vc-holder'

const agent = new Agent({
config: {
/* config */
},
dependencies: agentDependencies,
modules: {
openId4VcHolder: new OpenId4VcHolderModule(),
/* other custom modules */
},
})

await agent.initialize()
```

How the module is injected and the agent has been initialized, you can access the module's functionality through `agent.modules.openId4VcHolder`.

#### Preparing a DID

In order to request a credential, you'll need to provide a DID that the issuer will use for setting the credential subject. In the following snippet we create one for the sake of the example, but this can be any DID that has a _authentication verification method_ with key type `Ed25519`.

```ts
// first we create the DID
const did = await agent.dids.create<KeyDidCreateOptions>({
method: 'key',
options: {
keyType: KeyType.Ed25519,
},
})

// next we do some assertions and extract the key identifier (kid)

if (
!did.didState.didDocument ||
!did.didState.didDocument.authentication ||
did.didState.didDocument.authentication.length === 0
) {
throw new Error("Error creating did document, or did document has no 'authentication' verificationMethods")
}

const [verificationMethod] = did.didState.didDocument.authentication
const kid = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id
```

#### Requesting the credential (Pre-Authorized)

Now a credential issuance can be requested as follows.

```ts
const w3cCredentialRecord = await agent.modules.openId4VcHolder.requestCredentialPreAuthorized({
issuerUri,
kid,
checkRevocationState: false,
})

console.log(w3cCredentialRecord)
```

#### Full example

```ts
import { OpenId4VcHolderModule } from '@aries-framework/openid4vc-holder'
import { agentDependencies } from '@aries-framework/node' // use @aries-framework/react-native for React Native
import { Agent, KeyDidCreateOptions } from '@aries-framework/core'

const run = async () => {
const issuerUri = '' // The obtained issuer URI

// Create the Agent
const agent = new Agent({
config: {
/* config */
},
dependencies: agentDependencies,
modules: {
openId4VcHolder: new OpenId4VcHolderModule(),
/* other custom modules */
},
})

// Initialize the Agent
await agent.initialize()

// Create a DID
const did = await agent.dids.create<KeyDidCreateOptions>({
method: 'key',
options: {
keyType: KeyType.Ed25519,
},
})

// Assert DIDDocument is valid
if (
!did.didState.didDocument ||
!did.didState.didDocument.authentication ||
did.didState.didDocument.authentication.length === 0
) {
throw new Error("Error creating did document, or did document has no 'authentication' verificationMethods")
}

// Extract key identified (kid) for authentication verification method
const [verificationMethod] = did.didState.didDocument.authentication
const kid = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id

// Request the credential
const w3cCredentialRecord = await agent.modules.openId4VcHolder.requestCredentialPreAuthorized({
issuerUri,
kid,
checkRevocationState: false,
})

// Log the received credential
console.log(w3cCredentialRecord)
}
```
14 changes: 14 additions & 0 deletions packages/openid4vc-holder/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Config } from '@jest/types'

import base from '../../jest.config.base'

import packageJson from './package.json'

const config: Config.InitialOptions = {
...base,

displayName: packageJson.name,
setupFilesAfterEnv: ['./tests/setup.ts'],
}

export default config
43 changes: 43 additions & 0 deletions packages/openid4vc-holder/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@aries-framework/openid4vc-holder",
"main": "build/index",
"types": "build/index",
"version": "0.4.2",
"files": [
"build"
],
"license": "Apache-2.0",
"publishConfig": {
"access": "public"
},
"homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/openid4vc-holder",
"repository": {
"type": "git",
"url": "https://github.com/hyperledger/aries-framework-javascript",
"directory": "packages/openid4vc-holder"
},
"scripts": {
"build": "yarn run clean && yarn run compile",
"clean": "rimraf ./build",
"compile": "tsc -p tsconfig.build.json",
"prepublishOnly": "yarn run build",
"test": "jest"
},
"dependencies": {
"@aries-framework/core": "0.4.2",
"@sphereon/oid4vci-client": "^0.7.3",
"@sphereon/oid4vci-common": "^0.7.3",
"@sphereon/ssi-types": "^0.17.5",
"@stablelib/random": "^1.0.2",
"fast-text-encoding": "^1.0.6"
},
"devDependencies": {
"@aries-framework/askar": "0.4.2",
"@aries-framework/node": "0.4.2",
"@hyperledger/aries-askar-nodejs": "^0.1.0",
"@types/jsonpath": "^0.2.0",
"nock": "^13.3.0",
"rimraf": "^4.4.0",
"typescript": "~4.9.5"
}
}
53 changes: 53 additions & 0 deletions packages/openid4vc-holder/src/OpenId4VcHolderApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type {
GenerateAuthorizationUrlOptions,
PreAuthCodeFlowOptions,
AuthCodeFlowOptions,
} from './OpenId4VcHolderServiceOptions'
import type { W3cCredentialRecord } from '@aries-framework/core'

import { injectable, AgentContext } from '@aries-framework/core'

import { OpenId4VcHolderService } from './OpenId4VcHolderService'
import { AuthFlowType } from './OpenId4VcHolderServiceOptions'

/**
* @public
*/
@injectable()
export class OpenId4VcHolderApi {
private agentContext: AgentContext
private openId4VcHolderService: OpenId4VcHolderService

public constructor(agentContext: AgentContext, openId4VcHolderService: OpenId4VcHolderService) {
this.agentContext = agentContext
this.openId4VcHolderService = openId4VcHolderService
}

public async requestCredentialUsingPreAuthorizedCode(
options: PreAuthCodeFlowOptions
): Promise<W3cCredentialRecord[]> {
// set defaults
const verifyRevocationState = options.verifyCredentialStatus ?? true

return this.openId4VcHolderService.requestCredential(this.agentContext, {
...options,
verifyCredentialStatus: verifyRevocationState,
flowType: AuthFlowType.PreAuthorizedCodeFlow,
})
}

public async requestCredentialUsingAuthorizationCode(options: AuthCodeFlowOptions): Promise<W3cCredentialRecord[]> {
// set defaults
const checkRevocationState = options.verifyCredentialStatus ?? true

return this.openId4VcHolderService.requestCredential(this.agentContext, {
...options,
verifyCredentialStatus: checkRevocationState,
flowType: AuthFlowType.AuthorizationCodeFlow,
})
}

public async generateAuthorizationUrl(options: GenerateAuthorizationUrlOptions) {
return this.openId4VcHolderService.generateAuthorizationUrl(options)
}
}
31 changes: 31 additions & 0 deletions packages/openid4vc-holder/src/OpenId4VcHolderModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { DependencyManager, Module } from '@aries-framework/core'

import { AgentConfig } from '@aries-framework/core'

import { OpenId4VcHolderApi } from './OpenId4VcHolderApi'
import { OpenId4VcHolderService } from './OpenId4VcHolderService'

/**
* @public
*/
export class OpenId4VcHolderModule implements Module {
public readonly api = OpenId4VcHolderApi

/**
* Registers the dependencies of the question answer module on the dependency manager.
*/
public register(dependencyManager: DependencyManager) {
// Warn about experimental module
dependencyManager
.resolve(AgentConfig)
.logger.warn(
"The '@aries-framework/openid4vc-holder' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages."
)

// Api
dependencyManager.registerContextScoped(OpenId4VcHolderApi)

// Services
dependencyManager.registerSingleton(OpenId4VcHolderService)
}
}
Loading

0 comments on commit 14c61ff

Please sign in to comment.