Skip to content

Commit

Permalink
feat: test credentials
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <[email protected]>
  • Loading branch information
TimoGlastra committed Oct 30, 2024
1 parent b8cd196 commit 12610b7
Show file tree
Hide file tree
Showing 27 changed files with 651 additions and 407 deletions.
Binary file added agent/assets/erika.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
"dependencies": {
"@credo-ts/askar": "https://gitpkg.vercel.app/animo/aries-framework-javascript/packages/askar?funke",
"@credo-ts/core": "https://gitpkg.vercel.app/animo/aries-framework-javascript/packages/core?funke",
"@credo-ts/openid4vc": "https://gitpkg.vercel.app/animo/aries-framework-javascript/packages/openid4vc?funke",
"@credo-ts/node": "https://gitpkg.vercel.app/animo/aries-framework-javascript/packages/node?funke",
"@credo-ts/openid4vc": "https://gitpkg.vercel.app/animo/aries-framework-javascript/packages/openid4vc?funke",
"@hyperledger/aries-askar-nodejs": "^0.2.3",
"@protokoll/mdoc-client": "^0.2.35",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.2",
Expand Down
36 changes: 18 additions & 18 deletions agent/src/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,30 @@ import express, { type NextFunction, type Request, type Response } from 'express
import z from 'zod'
import { agent } from './agent'
import { AGENT_HOST } from './constants'
import { getIssuer } from './issuer'
import { credentialSupportedIds } from './issuerMetadata'
import { getIssuerIdForCredentialConfigurationId } from './issuer'
import { issuers } from './issuers'
import { getX509Certificate } from './keyMethods'
import { getVerifier } from './verifier'

const zCreateOfferRequest = z.object({
// FIXME: rename offeredCredentials to credentialSupportedIds in AFJ
credentialSupportedIds: z.array(z.enum(credentialSupportedIds)),
credentialSupportedIds: z.array(z.string()),
issuerId: z.string(),
})

export const apiRouter = express.Router()

apiRouter.use(express.json())
apiRouter.use(express.text())
apiRouter.post('/offers/create', async (request: Request, response: Response) => {
const issuer = await getIssuer()
// FIXME: somehow JSON doesn't work
const createOfferRequest = zCreateOfferRequest.parse(
typeof request.body === 'string' ? JSON.parse(request.body) : request.body
)
const issuerId = getIssuerIdForCredentialConfigurationId(createOfferRequest.credentialSupportedIds[0])

const offer = await agent.modules.openId4VcIssuer.createCredentialOffer({
issuerId: issuer.issuerId,
issuerId,
offeredCredentials: createOfferRequest.credentialSupportedIds,
preAuthorizedCodeFlowConfig: {
userPinRequired: false,
Expand All @@ -56,20 +57,19 @@ apiRouter.get('/x509', async (_, response: Response) => {
})

apiRouter.get('/issuer', async (_, response: Response) => {
const issuer = await getIssuer()

return response.json({
credentialsSupported: issuer.credentialsSupported.map((c) => {
const displayName =
c.display?.[0]?.name ??
(c.format === 'vc+sd-jwt' ? c.vct : c.format === 'mso_mdoc' ? c.doctype : 'Unregistered format')

return {
display: `${displayName} (${c.format})`,
id: c.id,
}
}),
display: issuer.display,
credentialsSupported: issuers.flatMap((i) =>
i.credentialsSupported.map((c) => {
const displayName =
c.display?.[0]?.name ??
(c.format === 'vc+sd-jwt' ? c.vct : c.format === 'mso_mdoc' ? c.doctype : 'Unregistered format')

return {
display: `${i.display[0].name} - ${displayName} (${c.format})`,
id: c.id,
}
})
),
availableX509Certificates: [AGENT_HOST],
})
})
Expand Down
194 changes: 30 additions & 164 deletions agent/src/issuer.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
import type { KeyType } from '@credo-ts/core'
import { ClaimFormat } from '@credo-ts/core'
import type {
OpenId4VciCreateIssuerOptions,
OpenId4VciCredentialRequestToCredentialMapper,
OpenId4VciSignMdocCredential,
OpenId4VciSignSdJwtCredential,
} from '@credo-ts/openid4vc'
import { agent } from './agent'
import { AGENT_HOST } from './constants'
import {
credentialsSupported,
issuerDisplay,
mockEmployeeBadgeMdoc,
mockIdenticonAttendeeSdJwt,
mockPidOpenId4VcPlaygroundCredentialMsoMdocJwk,
mockPidOpenId4VcPlaygroundCredentialSdJwtVcJwk,
} from './issuerMetadata'
import { issuers, issuersCredentialsData } from './issuers'
import { getX509Certificate } from './keyMethods'

const issuerId = 'e451c49f-1186-4fe4-816d-a942151dfd59'

export async function createIssuer() {
return agent.modules.openId4VcIssuer.createIssuer({
issuerId,
credentialsSupported,
display: issuerDisplay,
})
export async function createOrUpdateIssuer(options: OpenId4VciCreateIssuerOptions & { issuerId: string }) {
if (await doesIssuerExist(options.issuerId)) {
await agent.modules.openId4VcIssuer.updateIssuerMetadata(options)
} else {
return agent.modules.openId4VcIssuer.createIssuer(options)
}
}

export async function doesIssuerExist() {
export async function doesIssuerExist(issuerId: string) {
try {
await agent.modules.openId4VcIssuer.getIssuerByIssuerId(issuerId)
return true
Expand All @@ -35,16 +27,20 @@ export async function doesIssuerExist() {
}
}

export async function getIssuer() {
export async function getIssuer(issuerId: string) {
return agent.modules.openId4VcIssuer.getIssuerByIssuerId(issuerId)
}

export async function updateIssuer() {
await agent.modules.openId4VcIssuer.updateIssuerMetadata({
issuerId,
credentialsSupported,
display: issuerDisplay,
})
export function getIssuerIdForCredentialConfigurationId(credentialConfigurationId: string) {
const issuer = issuers.find(({ credentialsSupported }) =>
credentialsSupported.find((s) => s.id === credentialConfigurationId)
)

if (!issuer) {
throw new Error(`Issuer not found for credential configuration id ${credentialConfigurationId}`)
}

return issuer.issuerId
}

export const credentialRequestToCredentialMapper: OpenId4VciCredentialRequestToCredentialMapper = async ({
Expand All @@ -56,159 +52,29 @@ export const credentialRequestToCredentialMapper: OpenId4VciCredentialRequestToC
const credentialConfigurationId = credentialConfigurationIds[0]

const x509Certificate = getX509Certificate()

let holderKeyType: KeyType
if (holderBinding.method === 'jwk') {
holderKeyType = holderBinding.jwk.keyType
} else {
throw new Error(`Unsupported holder binding method: ${holderBinding.method}`)
}

if (credentialConfigurationId === mockPidOpenId4VcPlaygroundCredentialSdJwtVcJwk.id) {
return {
credentialSupportedId: mockPidOpenId4VcPlaygroundCredentialSdJwtVcJwk.id,
format: 'vc+sd-jwt',
holder: holderBinding,
payload: {
vct: mockPidOpenId4VcPlaygroundCredentialSdJwtVcJwk.vct,
given_name: 'Erika',
family_name: 'Mustermann',
birthdate: '1963-08-12',
source_document_type: 'id_card',
address: {
street_address: 'Heidestraße 17',
locality: 'Köln',
postal_code: '51147',
country: 'DE',
},
nationalities: ['DE'],
gender: 'female',
birth_family_name: 'Gabler',
place_of_birth: {
locality: 'Berlin',
country: 'DE',
},
also_known_as: 'Schwester Agnes',
age_equal_or_over: {
'12': true,
'14': true,
'16': true,
'18': true,
'21': true,
'65': false,
},
},
issuer: {
method: 'x5c',
x5c: [x509Certificate],
issuer: AGENT_HOST,
},
disclosureFrame: {
_sd: [
'given_name',
'family_name',
'birthdate',
'source_document_type',
'address',
'nationalities',
'gender',
'birth_family_name',
'place_of_birth',
'also_known_as',
],
age_equal_or_over: { _sd: ['12', '14', '16', '18', '21', '65'] },
},
} as const satisfies OpenId4VciSignSdJwtCredential
const credentialData = issuersCredentialsData[credentialConfigurationId as keyof typeof issuersCredentialsData]
if (!credentialData) {
throw new Error(`Unsupported credential configuration id ${credentialConfigurationId}`)
}

if (credentialConfigurationId === mockIdenticonAttendeeSdJwt.id) {
if (credentialData.format === ClaimFormat.SdJwtVc) {
return {
credentialSupportedId: mockIdenticonAttendeeSdJwt.id,
format: 'vc+sd-jwt',
...credentialData,
holder: holderBinding,
payload: {
vct: mockIdenticonAttendeeSdJwt.vct,
first_name: 'Erika',
last_name: 'Mustermann',
sponsorship_tier: 'Platinum',
},
issuer: {
method: 'x5c',
x5c: [x509Certificate],
issuer: AGENT_HOST,
},
disclosureFrame: {
_sd: ['first_name', 'last_name'],
},
} as const
} satisfies OpenId4VciSignSdJwtCredential
}

if (credentialConfigurationId === mockPidOpenId4VcPlaygroundCredentialMsoMdocJwk.id) {
if (credentialData.format === ClaimFormat.MsoMdoc) {
return {
credentialSupportedId: mockPidOpenId4VcPlaygroundCredentialMsoMdocJwk.id,
format: 'mso_mdoc',
...credentialData,
holderKey: holderBinding.key,
docType: mockPidOpenId4VcPlaygroundCredentialMsoMdocJwk.doctype,
issuerCertificate: x509Certificate,
namespaces: {
'eu.europa.ec.eudi.pid.1': {
resident_country: 'DE',
age_over_12: true,
family_name_birth: 'GABLER',
given_name: 'ERIKA',
age_birth_year: 1984,
age_over_18: true,
age_over_21: true,
resident_city: 'KÖLN',
family_name: 'MUSTERMANN',
birth_place: 'BERLIN',
expiry_date: new Date('2024-08-26T14:49:42.124Z'),
issuing_country: 'DE',
age_over_65: false,
issuance_date: new Date('2024-08-12T14:49:42.124Z'),
resident_street: 'HEIDESTRASSE 17',
age_over_16: true,
resident_postal_code: '51147',
birth_date: '1984-01-26',
issuing_authority: 'DE',
age_over_14: true,
age_in_years: 40,
nationality: new Map([
['value', 'DE'],
['countryName', 'Germany'],
]),
},
},
validityInfo: {
validUntil: new Date('2025-08-26T14:49:42.124Z'),
validFrom: new Date('2024-08-12T14:49:42.124Z'),
signed: new Date(),
},
} as const satisfies OpenId4VciSignMdocCredential
}

if (credentialConfigurationId === mockEmployeeBadgeMdoc.id) {
return {
credentialSupportedId: mockEmployeeBadgeMdoc.id,
format: 'mso_mdoc',
holderKey: holderBinding.key,
docType: mockEmployeeBadgeMdoc.doctype,
issuerCertificate: x509Certificate,
namespaces: {
[mockEmployeeBadgeMdoc.doctype]: {
is_admin: true,
last_name: 'Mustermann',
first_name: 'Erika',
department: 'Sales',
employee_id: '181888100',
},
},
validityInfo: {
validUntil: new Date('2025-08-26T14:49:42.124Z'),
validFrom: new Date('2024-08-12T14:49:42.124Z'),
signed: new Date(),
},
} as const satisfies OpenId4VciSignMdocCredential
} satisfies OpenId4VciSignMdocCredential
}

throw new Error(`Unsupported credential ${credentialConfigurationId}`)
Expand Down
Loading

0 comments on commit 12610b7

Please sign in to comment.