Skip to content

Commit

Permalink
feat: Add verida connector (#227)
Browse files Browse the repository at this point in the history
* feat: Init verida connector

* feat: Add verida packages
  • Loading branch information
DaevMithran authored May 25, 2023
1 parent 5d61791 commit c1ac128
Show file tree
Hide file tree
Showing 10 changed files with 2,797 additions and 165 deletions.
2,756 changes: 2,598 additions & 158 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
"@veramo/did-resolver": "^5.2.0",
"@veramo/key-manager": "^5.1.2",
"@veramo/kms-local": "^5.1.2",
"@verida/account-node": "^2.3.4",
"@verida/client-ts": "^2.3.4",
"@verida/types": "^2.3.1",
"cors": "^2.8.5",
"did-resolver": "^4.1.0",
"dotenv": "^16.0.3",
Expand Down
18 changes: 17 additions & 1 deletion src/controllers/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import { check, validationResult } from 'express-validator'

import { Credentials } from '../services/credentials.js'
import { CustomerService } from '../services/customer.js'
import { Credential } from '../types/types.js'

import * as dotenv from 'dotenv'
import { VeridaService } from '../services/connectors/verida.js'
dotenv.config()

const { USE_VERIDA_CONNECTOR } = process.env

export class CredentialController {

Expand Down Expand Up @@ -36,7 +43,16 @@ export class CredentialController {
error: `Issuer DID ${request.body.issuerDid} not found`
})
}
response.status(200).json(await Credentials.instance.issue_credential(request.body, response.locals.customerId))
const credential: Credential = await Credentials.instance.issue_credential(request.body, response.locals.customerId)
if (USE_VERIDA_CONNECTOR && credential.credentialSubject.id && credential.credentialSubject.id.startsWith('did:vda')) {
await VeridaService.instance.sendCredential(
credential.credentialSubject.id,
"New Verifiable Credential",
credential,
request.body.credentialName
)
}
response.status(200).json(credential)
} catch (error) {
return response.status(500).json({
error: `${error}`
Expand Down
5 changes: 2 additions & 3 deletions src/middleware/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class Authentication {
if (jwtRequest.path == '/' || jwtRequest.path == '/swagger') return next()

try {
const token = extractBearerTokenFromHeaders(jwtRequest.headers);
const token = extractBearerTokenFromHeaders(jwtRequest.headers)

const { payload } = await jwtVerify(
token, // The raw Bearer Token extracted from the request header
Expand All @@ -72,8 +72,7 @@ export class Authentication {
);

// custom payload logic
response.locals.customerId = payload.sub;

response.locals.customerId = payload.sub
next()
} catch (err) {
return response.status(500).send({
Expand Down
136 changes: 136 additions & 0 deletions src/services/connectors/verida.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { EnvironmentType } from '@verida/types'
import { Context, Network } from '@verida/client-ts'
import { AutoAccount } from '@verida/account-node'

import { Credential } from '../../types/types.js'
import { CredentialDataRecord, DataRecord } from '../../types/verida.js'
import { POLYGON_RPC_URL, VERIDA_CREDENTIAL_RECORD_SCHEMA } from '../../types/constants.js'

import * as dotenv from 'dotenv'
dotenv.config()

const { VERIDA_NETWORK, VERIDA_APP_NAME, ISSUER_VERIDA_PRIVATE_KEY, POLYGON_PRIVATE_KEY } = process.env

/**
* Helper class for the Verida protocol.
*
* Run the init method before running any other method.
*/
export class VeridaService {
private context?: Context
private account?: AutoAccount

static instance = new VeridaService()

/**
* Initialise the Verida account and context.
*
* @param environment The Verida environment.
* @param contextName The Context name of the application.
* @param accountPrivateKey The private key of the account
*/
async init(
environment: EnvironmentType,
contextName: string,
accountPrivateKey: string,
polygonPrivateKey: string
) {
if(this.context && this.account) {
return
}
this.account = new AutoAccount({
privateKey: accountPrivateKey,
environment,
didClientConfig: {
callType: 'web3',
web3Config: {
rpcUrl: POLYGON_RPC_URL,
privateKey: polygonPrivateKey, // Polygon private key for creating DID, not needed in our case but required in the current version of the config.
},
},
})
try {
this.context = await Network.connect({
client: {
environment,
},
context: {
name: contextName,
},
account: this.account,
})
} catch (error) {
console.log(error)
throw new Error(`Error: ${error}`)
}
}

/**
* Send data to a DID via the Verida protocol.
*
* @param recipientDid The DID of the recipient.
* @param subject The subject of the message (similar to an email subject).
* @param data The data to be sent.
*/
async sendData(recipientDid: string, subject: string, data: DataRecord) {
try {
if(!this.context) {
await VeridaService.instance.init(
VERIDA_NETWORK,
VERIDA_APP_NAME,
ISSUER_VERIDA_PRIVATE_KEY,
POLYGON_PRIVATE_KEY
)
}

const messagingClient = await this.context!.getMessaging()

const messageType = 'inbox/type/dataSend' // There are different types of message, here we are sending some data.
const messageData = {
data: [data],
}
const messageConfig = {
recipientContextName: 'Verida: Vault', // The inbox of a DID is on the 'Verida: Vault' context. This context is the private space of this DID.
did: recipientDid,
}

await messagingClient.send(
recipientDid,
messageType,
messageData,
subject,
messageConfig
)
} catch (error) {
throw new Error(`Error sending data ${error}`)
}
}

/**
* Send a Verifiable Credential to a DID via the Verida protocol.
*
* @param recipientDid The DID of the recipient.
* @param messageSubject The subject of the message in which the Credential will be sent to the recipient (similar to an email subject).
* @param credential The credential itself.
* @param credentialName The name of the credential. For instance, will be displayed in the Verida Wallet UI.
* @param credentialSummary A summary of the credential. For instance, will be displayed in the Verida Wallet UI.
*/
async sendCredential(
recipientDid: string,
messageSubject: string,
credential: Credential,
credentialName: string,
credentialSummary?: string
) {
// The Credential record is how Verida wrap the credential to store it on the Network. Check the JSdoc of the type and each property. They are following the Verida Credential Record schema.
const credentialRecord: CredentialDataRecord = {
name: credentialName,
summary: credentialSummary,
schema: VERIDA_CREDENTIAL_RECORD_SCHEMA,
didJwtVc: credential.proof.jwt,
credentialSchema: credential['@context'][0],
credentialData: credential,
}
await this.sendData(recipientDid, messageSubject, credentialRecord)
}
}
2 changes: 1 addition & 1 deletion src/services/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class Credentials {
}

const agent = await Identity.instance.create_agent(agentId)
const verifiable_credential: Omit<VerifiableCredential, 'vc'> = await agent.execute(
const verifiable_credential: VerifiableCredential = await agent.execute(
'createVerifiableCredential',
{
save: false,
Expand Down
8 changes: 8 additions & 0 deletions src/types/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ export const VC_TYPE: string = 'VerifiableCredential'
export const VC_PROOF_FORMAT = 'jwt'
export const VC_REMOVE_ORIGINAL_FIELDS = false
export const CORS_ERROR_MSG = 'The CORS policy for this site does not allow access from the specified Origin.'


// verida
export const POLYGON_RPC_URL = 'https://rpc-mumbai.maticvigil.com'

// Schema to store a Verifiable Credential on the Verida Network.
export const VERIDA_CREDENTIAL_RECORD_SCHEMA =
'https://common.schemas.verida.io/credential/base/v0.2.0/schema.json'
9 changes: 8 additions & 1 deletion src/types/environment.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@ declare global {
interface ProcessEnv {
MAINNET_RPC_URL: string
TESTNET_RPC_URL: string
RESOLVER_URL: string
RESOLVER_URL: string
ALLOWED_ORIGINS: string | undefined
ISSUER_DATABASE_URL: string
ISSUER_SECRET_KEY: string
ISSUER_DATABASE_CERT: string | undefined
OIDC_JWKS_ENDPOINT: string
AUDIENCE_ENDPOINT: string
OIDC_ISSUER: string

// verida
USE_VERIDA_CONNECTOR: boolean
VERIDA_APP_NAME: string
ISSUER_VERIDA_PRIVATE_KEY: string
POLYGON_PRIVATE_KEY: string
VERIDA_NETWORK: EnvironmentType
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export interface CredentialRequest {

export type GenericAuthUser = Record<string, any> | null | undefined

export type Credential = Omit<VerifiableCredential, 'vc'>
export type Credential = VerifiableCredential // Omit<VerifiableCredential, 'vc'>

const UUID = '([a-z,0-9,-]{36,36})'
const ID_CHAR = `(?:[a-zA-Z0-9]{21,22}|${UUID})`
Expand Down
23 changes: 23 additions & 0 deletions src/types/verida.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/** Structure of the record stored on the Verida Network */
export interface DataRecord {
/** Name/Title of the record, for instance used while browsing the UI. Optional. */
name?: string;
/** A summary of the data, could be displayed in the UI. Optional. */
summary?: string;
/** The schema of the record, For Credential data, it will be the Credential schema. Required. */
schema: string;
/** Any specific attributes of the record. These are following the schema mentioned above. */
[key: string]: unknown;
}

/** Structure of a Credential record stored on the Verida Network. */
export interface CredentialDataRecord extends DataRecord {
/** Name is mandatory */
name: string;
/** DID JWT of this credential */
didJwtVc: string;
/** Schema of the DID-JWT Verifiable Credential */
credentialSchema: string;
/** Data included in the DID-JWT Verifiable Credential */
credentialData: object;
}

0 comments on commit c1ac128

Please sign in to comment.