Skip to content

Commit

Permalink
feat: Introduce EBSI attestation service to get VCs, for instance to …
Browse files Browse the repository at this point in the history
…onboard
  • Loading branch information
nklomp committed Jun 19, 2024
1 parent 053cf59 commit 59f1809
Show file tree
Hide file tree
Showing 20 changed files with 1,162 additions and 518 deletions.
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,12 @@
"@veramo/url-handler": "4.2.0",
"@sphereon/ssi-types": "workspace:*",
"@sphereon/ssi-sdk.core": "workspace:*",
"@sphereon/oid4vci-common": "0.10.4-unstable.116",
"@sphereon/oid4vci-client": "0.10.4-unstable.116",
"@sphereon/oid4vci-issuer": "0.10.4-unstable.116",
"@sphereon/oid4vci-common": "file:../OID4VCI/packages/common",
"@sphereon/oid4vci-client": "file:../OID4VCI/packages/client",
"@sphereon/oid4vci-issuer": "file:../OID4VCI/packages/issuer",
"@sphereon/ssi-sdk-ext.did-resolver-ebsi": "0.20.1-unstable.3",
"@sphereon/ssi-sdk-ext.key-utils": "file:../SSI-SDK-crypto-extensions/packages/key-utils",
"@sphereon/ssi-sdk-ext.did-utils": "file:../SSI-SDK-crypto-extensions/packages/did-utils",
"@noble/hashes": "1.2.0",
"did-jwt": "6.11.6",
"did-jwt-vc": "3.1.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-config/src/agentCreator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TAgent, IPluginMethodMap, IAgentOptions } from '@veramo/core'
import { createObjects } from './objectCreator.js'
import { createObjects } from './objectCreator'
import yaml from 'yaml'

/**
Expand Down
119 changes: 119 additions & 0 deletions packages/ebsi-authorization-client/__tests__/attestation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { CreateRequestObjectMode } from '@sphereon/oid4vci-common'
import { toJwk } from '@sphereon/ssi-sdk-ext.key-utils'
import { createObjects, getConfig } from '@sphereon/ssi-sdk.agent-config'
import { IOID4VCIHolder } from '@sphereon/ssi-sdk.oid4vci-holder'
import { IPresentationExchange } from '@sphereon/ssi-sdk.presentation-exchange'
import { IDidAuthSiopOpAuthenticator } from '@sphereon/ssi-sdk.siopv2-oid4vp-op-auth'
import { IDIDManager, IIdentifier, IKeyManager, MinimalImportableKey, TAgent } from '@veramo/core'
// @ts-ignore
import cors from 'cors'

// @ts-ignore
import express, { Express } from 'express'
import { Server } from 'http'

import { DataSource } from 'typeorm'
import { ebsiCreateAttestationRequestAuthURL } from '../src/functions'

let dbConnection: Promise<DataSource>
let agent: TAgent<IKeyManager & IDIDManager & IDidAuthSiopOpAuthenticator & IPresentationExchange & IOID4VCIHolder>
let app: Express | undefined
let server: Server<any, any> | undefined
const port = 3333
const secp256k1PrivateKey: MinimalImportableKey = {
privateKeyHex: '6e491660cf923f7d9ce4a03401444b361817df9e76b926b55e21ffe7144d2ee6',
kms: 'local',
type: 'Secp256k1',
meta: {
purposes: ['capabilityInvocation'],
},
}
const secp256k1Jwk = toJwk(secp256k1PrivateKey.privateKeyHex, 'Secp256k1', { isPrivateKey: true })

const secp256r1PrivateKey: MinimalImportableKey = {
privateKeyHex: 'f0710a0bb80c28a14ae62831bfe7f90a6937d006295fad6115e5539e7e314ee4',
kms: 'local',
type: 'Secp256r1',
meta: {
purposes: ['assertionMethod', 'authentication'],
},
}

const secp256r1Jwk = toJwk(secp256r1PrivateKey.privateKeyHex, 'Secp256r1', { isPrivateKey: true })

const MOCK_BASE_URL = 'https://ebsi-sphereon.ngrok.dev' // `http://localhost:${port}`
jest.setTimeout(600000)

const setup = async (): Promise<boolean> => {
const config = await getConfig('packages/ebsi-authorization-client/agent.yml')
const { localAgent, db } = await createObjects(config, { localAgent: '/agent', db: '/dbConnection' })
agent = localAgent as TAgent<IKeyManager & IDIDManager & IDidAuthSiopOpAuthenticator & IPresentationExchange & IOID4VCIHolder>
dbConnection = db

app = express()
app.use(
cors({
origin: ['*'],
}),
)
app.use(express.json())

app.get('/', async (req: express.Request, res: express.Response) => {
res.send({ hello: 'world' })
})

app.get('/.well-known/jwks', async (req: express.Request, res: express.Response) => {
res.send({ keys: [{ ...secp256k1Jwk }, { ...secp256r1Jwk }] })
})

console.log(`########## $${MOCK_BASE_URL}`)

server = app.listen(port, () => {
console.log(`Mock server is listening to port ${port}`)
})

return true
}

const tearDown = async (): Promise<boolean> => {
if (server) {
server.closeAllConnections()
}

;(await dbConnection).close()
return true
}

describe('attestation client should', () => {
let identifier: IIdentifier
beforeAll(async (): Promise<void> => {
await setup()
identifier = await agent.didManagerCreate({ provider: 'did:ebsi', options: { secp256k1Key: secp256k1PrivateKey, secp256r1Key: secp256r1PrivateKey } })
console.log(identifier.did)
})

afterAll(async (): Promise<void> => {
await tearDown()
})

it('get attestation to onboard', async () => {
const clientId = MOCK_BASE_URL
const authUrl = await ebsiCreateAttestationRequestAuthURL(
{
credentialType: 'VerifiableAuthorisationToOnboard',
redirectUri: `${MOCK_BASE_URL}`,
clientId,
idOpts: { identifier },
credentialIssuer: 'https://conformance-test.ebsi.eu/conformance/v3/issuer-mock',
requestObjectOpts: {
iss: clientId,
requestObjectMode: CreateRequestObjectMode.REQUEST_OBJECT,
jwksUri: `${MOCK_BASE_URL}/.well-known/jwks`,
},
},
{ agent },
)

console.log(authUrl)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DataSource } from 'typeorm'

jest.setTimeout(60000)

import contactManagerAgentLogic from './shared/ebsiAuthorizationClientrAgentLogic'
import authClientAgentLogic from './shared/ebsiAuthorizationClientAgentLogic'

let dbConnection: Promise<DataSource>
let agent: any
Expand All @@ -30,5 +30,5 @@ const testContext = {
}

describe('Local integration tests', (): void => {
contactManagerAgentLogic(testContext)
authClientAgentLogic(testContext)
})
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { createAgent, IAgent, IAgentOptions, IDIDManager, IKeyManager } from '@v
import { AgentRestClient } from '@veramo/remote-client'
import { AgentRouter, RequestWithAgentRouter } from '@veramo/remote-server'
import { createObjects, getConfig } from '../../agent-config/src'
import contactManagerAgentLogic from './shared/ebsiAuthorizationClientrAgentLogic'
import authClientAgentLogic from './shared/ebsiAuthorizationClientAgentLogic'
import { IEBSIAuthorizationClient } from '../src'
import { IDidAuthSiopOpAuthenticator } from '@sphereon/ssi-sdk.siopv2-oid4vp-op-auth'

Expand Down Expand Up @@ -68,5 +68,5 @@ const testContext = {
}

describe('REST integration tests', (): void => {
contactManagerAgentLogic(testContext)
authClientAgentLogic(testContext)
})
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { OpenID4VCIClient } from '@sphereon/oid4vci-client'
import { Alg, AuthorizationDetails, CredentialIssuerMetadata, CredentialResponse, Jwt } from '@sphereon/oid4vci-common'
import { Alg, AuthorizationDetails, CredentialResponse, Jwt } from '@sphereon/oid4vci-common'
import { toJwk } from '@sphereon/ssi-sdk-ext.key-utils'
import { IDidAuthSiopOpAuthenticator } from '@sphereon/ssi-sdk.siopv2-oid4vp-op-auth'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
import { from } from '@trust/keyto'
import { IDIDManager, IIdentifier, IKeyManager, MinimalImportableKey, TAgent } from '@veramo/core'
import { fetch } from 'cross-fetch'
//@ts-ignore
Expand Down Expand Up @@ -151,12 +148,8 @@ export default (testContext: { getAgent: () => ConfiguredAgent; setup: () => Pro
// })).json()
//
// console.log(JSON.stringify(token))

// TODO acquire access token
client.endpointMetadata.credentialIssuerMetadata = {
...client.endpointMetadata.credentialIssuerMetadata,
authorization_endpoint: 'https://api-conformance.ebsi.eu/conformance/v3/auth-mock',
} as CredentialIssuerMetadata
client.endpointMetadata!.credentialIssuerMetadata!.authorization_endpoint =
client.endpointMetadata?.credentialIssuerMetadata?.authorization_endpoint ?? 'https://api-conformance.ebsi.eu/conformance/v3/auth-mock'

// FIXME fails with invalid code error
const accessToken = await client.acquireAccessToken({ code: parsedRequest.code })
Expand Down Expand Up @@ -242,7 +235,7 @@ export default (testContext: { getAgent: () => ConfiguredAgent; setup: () => Pro
.sign(importedJwk)
console.log(`URL: ${url}&request=${urlWithRequest}`)
/* const result = await fetch(`${url}&request=${urlWithRequest}`)
console.log(await result.text())*/
console.log(await result.text())*/
})

it.skip('Should retrieve the discovery metadata', async () => {
Expand Down
12 changes: 9 additions & 3 deletions packages/ebsi-authorization-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@
"@sphereon/ssi-sdk.presentation-exchange": "workspace:*",
"@sphereon/ssi-sdk.oid4vci-holder": "workspace:*",
"@sphereon/ssi-sdk.siopv2-oid4vp-op-auth": "workspace:*",
"@sphereon/ssi-sdk-ext.did-utils": "0.20.1-unstable.3",
"@sphereon/ssi-sdk.core": "workspace:*",
"@sphereon/ssi-types": "workspace:*",
"@sphereon/ssi-sdk-ext.did-provider-ebsi": "file:../../../SSI-SDK-crypto-extensions/packages/did-provider-ebsi",
"@sphereon/ssi-sdk-ext.key-utils": "file:../../../SSI-SDK-crypto-extensions/packages/key-utils",
"@veramo/core": "4.2.0",
"@veramo/utils": "4.2.0",
"cross-fetch": "^3.1.8",
"qs": "^6.11.2",
"uint8arrays": "^3.1.1",
Expand All @@ -31,21 +36,22 @@
"devDependencies": {
"@sphereon/oid4vci-client": "0.10.4-next.31",
"@sphereon/oid4vci-common": "0.10.4-next.31",
"@sphereon/ssi-sdk-ext.did-provider-ebsi": "0.20.1-unstable.3",
"tunnelmole": "^2.2.14",
"@sphereon/ssi-sdk.agent-config": "workspace:*",
"@sphereon/ssi-sdk-ext.key-manager": "0.20.1-unstable.3",
"@sphereon/ssi-sdk-ext.key-utils": "0.20.1-unstable.3",
"@sphereon/ssi-sdk-ext.kms-local": "0.20.1-unstable.3",
"@sphereon/ssi-sdk.agent-config": "workspace:*",
"@transmute/json-web-signature": "0.7.0-unstable.81",
"@types/express": "^4.17.21",
"@types/express-serve-static-core": "^4.19.1",
"@types/node": "^20.12.7",
"@types/qs": "^6.9.7",
"@types/cors": "^2.8.17",
"@veramo/data-store": "4.2.0",
"@veramo/key-manager": "4.2.0",
"@veramo/remote-client": "4.2.0",
"@veramo/remote-server": "4.2.0",
"express": "^4.19.2",
"cors": "^2.8.5",
"jose": "^5.3.0",
"typeorm": "^0.3.20"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { OID4VCICredentialFormat, RequestObjectOpts } from '@sphereon/oid4vci-common'
import { IIdentifierOpts } from '@sphereon/ssi-sdk-ext.did-utils'
import { AttestationAuthRequestUrlResult, ebsiCreateAttestationRequestAuthURL } from '../functions'
import { IRequiredContext } from '../types/IEBSIAuthorizationClient'

export class EBSIAttestationService {
private readonly credentialIssuer: string
private readonly idOpts: IIdentifierOpts
private readonly clientId: string

constructor(args: { credentialIssuer: string; idOpts: IIdentifierOpts; clientId: string }) {
this.credentialIssuer = args.credentialIssuer
this.idOpts = args.idOpts
this.clientId = args.clientId
}

createAttestationRequestAuthURL(
opts: {
credentialType: string
requestObjectOpts: RequestObjectOpts
redirectUri?: string
formats?: Array<Extract<OID4VCICredentialFormat, 'jwt_vc' | 'jwt_vc_json'>>
},
context: IRequiredContext,
): Promise<AttestationAuthRequestUrlResult> {
return ebsiCreateAttestationRequestAuthURL(
{
credentialIssuer: this.credentialIssuer,
idOpts: this.idOpts,
clientId: this.clientId,
...opts,
},
context,
)
}
}
Loading

0 comments on commit 59f1809

Please sign in to comment.