From ba3ac9c862293cfb946671ae6fceefab401c0f4b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 20 Nov 2024 13:24:45 +0100 Subject: [PATCH] add sixt Signed-off-by: Timo Glastra --- agent/src/endpoints.ts | 10 +++++-- agent/src/issuer.ts | 3 +- agent/src/server.ts | 7 +++-- agent/src/verifier.ts | 28 +++++++++++++----- agent/src/verifiers/animo.ts | 12 +++++--- agent/src/verifiers/sixt.ts | 12 +++++--- app/public/assets/verifiers/sixt/verifier.png | Bin 0 -> 14775 bytes 7 files changed, 51 insertions(+), 21 deletions(-) create mode 100644 app/public/assets/verifiers/sixt/verifier.png diff --git a/agent/src/endpoints.ts b/agent/src/endpoints.ts index b2e559c..f8d69a0 100644 --- a/agent/src/endpoints.ts +++ b/agent/src/endpoints.ts @@ -186,8 +186,6 @@ const zCreatePresentationRequestBody = z.object({ }) apiRouter.post('/requests/create', async (request: Request, response: Response) => { - const verifier = await getVerifier() - const createPresentationRequestBody = zCreatePresentationRequestBody.parse(request.body) const x509Certificate = getX509Certificate() @@ -200,6 +198,14 @@ apiRouter.post('/requests/create', async (request: Request, response: Response) }) } + const verifierId = verifiers.find((a) => a.presentationRequests.find((r) => r.id === definition.id))?.verifierId + if (!verifierId) { + return response.status(404).json({ + error: 'Verifier not found', + }) + } + const verifier = await getVerifier(verifierId) + const { authorizationRequest, verificationSession } = await agent.modules.openId4VcVerifier.createAuthorizationRequest({ verifierId: verifier.verifierId, diff --git a/agent/src/issuer.ts b/agent/src/issuer.ts index 46628fe..aea554c 100644 --- a/agent/src/issuer.ts +++ b/agent/src/issuer.ts @@ -15,6 +15,7 @@ import { getX509Certificate } from './keyMethods' import { DateOnly, oneYearInMilliseconds, serverStartupTimeInMilliseconds, tenDaysInMilliseconds } from './utils/date' import { getVerifier } from './verifier' import { pidSdJwtInputDescriptor } from './verifiers/util' +import { animoVerifier } from './verifiers/animo' export async function createOrUpdateIssuer(options: OpenId4VciCreateIssuerOptions & { issuerId: string }) { if (await doesIssuerExist(options.issuerId)) { @@ -51,7 +52,7 @@ export function getIssuerIdForCredentialConfigurationId(credentialConfigurationI export const getVerificationSessionForIssuanceSession: OpenId4VciGetVerificationSessionForIssuanceSessionAuthorization = async ({ agentContext, scopes }) => { - const verifier = await getVerifier() + const verifier = await getVerifier(animoVerifier.verifierId) const x509Certificate = getX509Certificate() const verifierApi = agentContext.dependencyManager.resolve(OpenId4VcVerifierApi) diff --git a/agent/src/server.ts b/agent/src/server.ts index f46c0f8..bd5a530 100644 --- a/agent/src/server.ts +++ b/agent/src/server.ts @@ -8,7 +8,8 @@ import { createOrUpdateIssuer } from './issuer' import { issuers } from './issuers' import { setupX509Certificate } from './keyMethods' import { getProvider, oidcRouterPath, oidcUrl } from './oidcProvider/provider' -import { createVerifier, doesVerifierExist } from './verifier' +import { createOrUpdateVerifier } from './verifier' +import { verifiers } from './verifiers' async function run() { await agent.initialize() @@ -28,8 +29,8 @@ async function run() { }) } - if (!(await doesVerifierExist())) { - await createVerifier() + for (const verifier of verifiers) { + await createOrUpdateVerifier(verifier) } await setupX509Certificate() diff --git a/agent/src/verifier.ts b/agent/src/verifier.ts index 3c9ab6e..039355d 100644 --- a/agent/src/verifier.ts +++ b/agent/src/verifier.ts @@ -1,14 +1,28 @@ +import type { OpenId4VcSiopCreateVerifierOptions } from '@credo-ts/openid4vc' import { agent } from './agent' +import type { DifPresentationExchangeDefinitionV2 } from '@credo-ts/core' -const verifierId = 'c01ea0f3-34df-41d5-89d1-50ef3d181855' +export interface PlaygroundVerifierOptions { + verifierId: string + clientMetadata?: OpenId4VcSiopCreateVerifierOptions['clientMetadata'] + presentationRequests: Array +} -export async function createVerifier() { - return agent.modules.openId4VcVerifier.createVerifier({ - verifierId, - }) +export async function createOrUpdateVerifier(options: PlaygroundVerifierOptions) { + if (await doesVerifierExist(options.verifierId)) { + await agent.modules.openId4VcVerifier.updateVerifierMetadata({ + verifierId: options.verifierId, + clientMetadata: options.clientMetadata, + }) + } else { + return agent.modules.openId4VcVerifier.createVerifier({ + clientMetadata: options.clientMetadata, + verifierId: options.verifierId, + }) + } } -export async function doesVerifierExist() { +export async function doesVerifierExist(verifierId: string) { try { await agent.modules.openId4VcVerifier.getVerifierByVerifierId(verifierId) return true @@ -17,6 +31,6 @@ export async function doesVerifierExist() { } } -export async function getVerifier() { +export async function getVerifier(verifierId: string) { return agent.modules.openId4VcVerifier.getVerifierByVerifierId(verifierId) } diff --git a/agent/src/verifiers/animo.ts b/agent/src/verifiers/animo.ts index 5d9186a..673c67d 100644 --- a/agent/src/verifiers/animo.ts +++ b/agent/src/verifiers/animo.ts @@ -1,7 +1,13 @@ -import type { DifPresentationExchangeDefinitionV2 } from '@credo-ts/core' import { pidMdocInputDescriptor, pidSdJwtInputDescriptor } from './util' +import type { PlaygroundVerifierOptions } from '../verifier' export const animoVerifier = { + clientMetadata: { + logo_uri: + 'https://camo.githubusercontent.com/e19cc0e590529e86cb0d39e2528d36284e5c2ac454cd038473c8bd71b2927720/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f616e696d6f2d736f6c7574696f6e732f696d6167652f75706c6f61642f76313635363537383332302f616e696d6f2d6c6f676f2d6c696768742d6e6f2d746578745f6f6b396175792e737667', + client_name: 'Animo', + }, + verifierId: 'd8662712-ee78-406c-a88c-df4ff1ed9468', presentationRequests: [ { id: '4db74328-9e94-49bb-97b7-bbfcb2d11a06', @@ -64,6 +70,4 @@ export const animoVerifier = { ], }, ], -} as const satisfies { - presentationRequests: Array -} +} as const satisfies PlaygroundVerifierOptions diff --git a/agent/src/verifiers/sixt.ts b/agent/src/verifiers/sixt.ts index 4ce00b5..876843f 100644 --- a/agent/src/verifiers/sixt.ts +++ b/agent/src/verifiers/sixt.ts @@ -1,8 +1,14 @@ -import type { DifPresentationExchangeDefinitionV2 } from '@credo-ts/core' import { mobileDriversLicenseMdoc, mobileDriversLicenseSdJwt } from '../issuers/infrastruktur' import { mdocInputDescriptor, pidSdJwtInputDescriptor, sdJwtInputDescriptor } from './util' +import type { PlaygroundVerifierOptions } from '../verifier' +import { AGENT_HOST } from '../constants' export const sixtVerifier = { + verifierId: 'c01ea0f3-34df-41d5-89d1-50ef3d181855', + clientMetadata: { + logo_uri: `${AGENT_HOST}/assets/verifiers/sixt/verifier.png`, + client_name: 'Sixt - Rent a Car', + }, presentationRequests: [ { id: '1ad8ea6e-ec51-4e14-b316-dd76a6275480', @@ -49,6 +55,4 @@ export const sixtVerifier = { ], }, ], -} as const satisfies { - presentationRequests: Array -} +} as const satisfies PlaygroundVerifierOptions diff --git a/app/public/assets/verifiers/sixt/verifier.png b/app/public/assets/verifiers/sixt/verifier.png new file mode 100644 index 0000000000000000000000000000000000000000..ca4280757bdc8d0112028e34a490fca90939b4fa GIT binary patch literal 14775 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 literal 0 HcmV?d00001