Skip to content

Commit

Permalink
temp
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <[email protected]>
  • Loading branch information
TimoGlastra committed Jan 8, 2024
1 parent c7d9ba7 commit b77a188
Show file tree
Hide file tree
Showing 48 changed files with 1,299 additions and 957 deletions.
166 changes: 78 additions & 88 deletions demo-openid/src/Issuer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ import type {
CredentialRequestToCredentialMapper,
CredentialSupported,
OfferedCredential,
IssuerEndpointConfig,
OpenId4VcIssuerRecord,
} from '@aries-framework/openid4vc'
import type e from 'express'

import { AskarModule } from '@aries-framework/askar'
import { W3cCredential, W3cCredentialSubject, W3cIssuer, w3cDate } from '@aries-framework/core'
import { OpenId4VcIssuerModule, OpenIdCredentialFormatProfile } from '@aries-framework/openid4vc'
import { SdJwtCredential, SdJwtVcModule } from '@aries-framework/sd-jwt-vc'
import { ariesAskar } from '@hyperledger/aries-askar-nodejs'
import { Router } from 'express'

import { BaseAgent } from './BaseAgent'
import { Output } from './OutputClass'
Expand All @@ -31,9 +29,7 @@ export const openBadgeCredential = {
export const universityDegreeCredentialSdJwt = {
id: 'UniversityDegreeCredential-sdjwt',
format: OpenIdCredentialFormatProfile.SdJwtVc,
credential_definition: {
vct: 'UniversityDegreeCredential',
},
vct: 'UniversityDegreeCredential',
} satisfies CredentialSupported & { id: string }

export const credentialsSupported = [
Expand All @@ -42,103 +38,97 @@ export const credentialsSupported = [
universityDegreeCredentialSdJwt,
] satisfies CredentialSupported[]

function getOpenIdIssuerModules() {
return {
sdJwtVc: new SdJwtVcModule(),
askar: new AskarModule({ ariesAskar }),
openId4VcIssuer: new OpenId4VcIssuerModule({
issuerMetadata: {
issuerBaseUrl: 'http://localhost:2000',
tokenEndpointPath: '/token',
credentialEndpointPath: '/credentials',
credentialsSupported,
},
}),
} as const
function getCredentialRequestToCredentialMapper({
did,
kid,
}: {
did: string
kid: string
}): CredentialRequestToCredentialMapper {
return async ({ credentialRequest, holderDid, holderDidUrl }) => {
if (credentialRequest.format === 'jwt_vc_json' && credentialRequest.types.includes('UniversityDegreeCredential')) {
return new W3cCredential({
type: universityDegreeCredential.types,
issuer: new W3cIssuer({ id: did }),
credentialSubject: new W3cCredentialSubject({ id: holderDid }),
issuanceDate: w3cDate(Date.now()),
})
}

if (credentialRequest.format === 'jwt_vc_json' && credentialRequest.types.includes('OpenBadgeCredential')) {
return new W3cCredential({
type: openBadgeCredential.types,
issuer: new W3cIssuer({ id: did }),
credentialSubject: new W3cCredentialSubject({ id: holderDid }),
issuanceDate: w3cDate(Date.now()),
})
}

// FIXME: update to SD-JWT VC (not just SD-JWT)
if (credentialRequest.format === 'vc+sd-jwt' && credentialRequest.vct === 'UniversityDegreeCredential') {
return new SdJwtCredential({
payload: { type: 'UniversityDegreeCredential', university: 'innsbruck', degree: 'bachelor' },
holderDidUrl,
issuerDidUrl: kid,
disclosureFrame: { university: true, degree: true },
})
}

throw new Error('Invalid request')
}
}

export class Issuer extends BaseAgent<ReturnType<typeof getOpenIdIssuerModules>> {
export class Issuer extends BaseAgent<{
sdJwtVc: SdJwtVcModule
askar: AskarModule
openId4VcIssuer: OpenId4VcIssuerModule
}> {
public issuerRecord!: OpenId4VcIssuerRecord

public constructor(port: number, name: string) {
super({ port, name, modules: getOpenIdIssuerModules() })
const issuerModule = new OpenId4VcIssuerModule({
baseUrl: 'http://localhost:2000',
endpoints: {
credential: {
credentialRequestToCredentialMapper: (...args) =>
getCredentialRequestToCredentialMapper({ did: this.did, kid: this.kid })(...args),
},
},
})

super({
port,
name,
modules: {
sdJwtVc: new SdJwtVcModule(),
askar: new AskarModule({ ariesAskar }),
openId4VcIssuer: issuerModule,
} as const,
})

// FIXME: module is often added inline, should we add the router to either the config or the API?
this.app.use('/', issuerModule.router)
}

public static async build(): Promise<Issuer> {
const issuer = new Issuer(2000, 'OpenId4VcIssuer ' + Math.random().toString())
await issuer.initializeAgent('96213c3d7fc8d4d6754c7a0fd969598f')
issuer.issuerRecord = await issuer.agent.modules.openId4VcIssuer.createIssuer({
credentialsSupported,
})

return issuer
}

public async configureRouter(): Promise<e.Router> {
const endpointConfig: IssuerEndpointConfig = {
basePath: '/',
metadataEndpointConfig: { enabled: true },
accessTokenEndpointConfig: {
enabled: true,
verificationMethod: this.verificationMethod,
preAuthorizedCodeExpirationDuration: 100,
},
credentialEndpointConfig: {
enabled: true,
verificationMethod: this.verificationMethod,
credentialRequestToCredentialMapper: await this.getCredentialRequestToCredentialMapper(),
},
}

const router = await this.agent.modules.openId4VcIssuer.configureRouter(Router(), endpointConfig)
this.app.use('/', router)
return router
}

public getCredentialRequestToCredentialMapper(): CredentialRequestToCredentialMapper {
return async ({ credentialRequest, holderDid, holderDidUrl }) => {
if (
credentialRequest.format === 'jwt_vc_json' &&
credentialRequest.types.includes('UniversityDegreeCredential')
) {
return new W3cCredential({
type: universityDegreeCredential.types,
issuer: new W3cIssuer({ id: this.did }),
credentialSubject: new W3cCredentialSubject({ id: holderDid }),
issuanceDate: w3cDate(Date.now()),
})
}

if (credentialRequest.format === 'jwt_vc_json' && credentialRequest.types.includes('OpenBadgeCredential')) {
return new W3cCredential({
type: openBadgeCredential.types,
issuer: new W3cIssuer({ id: this.did }),
credentialSubject: new W3cCredentialSubject({ id: holderDid }),
issuanceDate: w3cDate(Date.now()),
})
}

if (
credentialRequest.format === 'vc+sd-jwt' &&
credentialRequest.credential_definition.vct === 'UniversityDegreeCredential'
) {
return new SdJwtCredential({
payload: { type: 'UniversityDegreeCredential', university: 'innsbruck', degree: 'bachelor' },
holderDidUrl,
issuerDidUrl: this.kid,
disclosureFrame: { university: true, degree: true },
})
}

throw new Error('Invalid request')
}
}

public async createCredentialOffer(offeredCredentials: OfferedCredential[]) {
const { credentialOfferRequest } = await this.agent.modules.openId4VcIssuer.createCredentialOfferAndRequest(
const { credentialOfferUri } = await this.agent.modules.openId4VcIssuer.createCredentialOffer({
issuerId: this.issuerRecord.id,
offeredCredentials,
{
scheme: 'openid-credential-offer',
preAuthorizedCodeFlowConfig: { userPinRequired: false },
}
)
scheme: 'openid-credential-offer',
preAuthorizedCodeFlowConfig: { userPinRequired: false },
})

return credentialOfferRequest
return credentialOfferUri
}

public async exit() {
Expand Down
3 changes: 1 addition & 2 deletions demo-openid/src/IssuerInquirer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export class IssuerInquirer extends BaseInquirer {

public static async build(): Promise<IssuerInquirer> {
const issuer = await Issuer.build()
await issuer.configureRouter()
return new IssuerInquirer(issuer)
}

Expand Down Expand Up @@ -60,7 +59,7 @@ export class IssuerInquirer extends BaseInquirer {
const choice = await prompt([this.inquireOptions(credentialsSupported.map((credential) => credential.id))])
const offeredCredential = credentialsSupported.find((credential) => credential.id === choice.options)
if (!offeredCredential) throw new Error(`No credential of type ${choice.options} found, that can be offered.`)
const offerRequest = await this.issuer.createCredentialOffer([offeredCredential])
const offerRequest = await this.issuer.createCredentialOffer([offeredCredential.id])

console.log(purpleText(`credential offer request: '${offerRequest}'`))
}
Expand Down
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,15 @@
"ws": "^8.13.0"
},
"resolutions": {
"@types/node": "18.18.8"
"@types/node": "18.18.8",
"@sphereon/ssi-types": "^0.17.6-unstable.69",
"@sphereon/oid4vci-client": "file:sphereon-oid4vci-client-0.8.1.tgz",
"@sphereon/oid4vci-common": "file:sphereon-oid4vci-common-0.8.1.tgz",
"@sphereon/oid4vci-issuer-server": "file:sphereon-oid4vci-issuer-server-0.8.1.tgz",
"@sphereon/oid4vci-issuer": "file:sphereon-oid4vci-issuer-0.8.1.tgz",
"@sphereon/did-auth-siop": "file:sphereon-did-auth-siop-v0.5.0-unstable.8.tgz",
"@sphereon/pex": "file:sphereon-pex-v2.2.3-unstable.12.tgz",
"@sphereon/pex-models": "^2.1.2"
},
"engines": {
"node": ">=18"
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export { Repository } from './storage/Repository'
export * from './storage/RepositoryEvents'
export { StorageService, Query, SimpleQuery, BaseRecordConstructor } from './storage/StorageService'
export * from './storage/migration'
export { getDirFromFilePath } from './utils/path'
export { getDirFromFilePath, joinUriParts } from './utils/path'
export { InjectionSymbols } from './constants'
export * from './wallet'
export type { TransportSession } from './agent/TransportService'
Expand Down
16 changes: 16 additions & 0 deletions packages/core/src/utils/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,19 @@
export function getDirFromFilePath(path: string) {
return path.substring(0, Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')))
}

/**
* Combine multiple uri parts into a single uri taking into account slashes.
*
* @param parts the parts to combine
* @returns the combined url
*/
export function joinUriParts(parts: string[]) {
let combined = ''

for (const part of parts) {
combined += part.endsWith('/') ? part : `${part}/`
}

return combined
}
16 changes: 8 additions & 8 deletions packages/openid4vc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@
"dependencies": {
"@aries-framework/askar": "^0.4.2",
"@aries-framework/core": "0.4.2",
"@sphereon/ssi-types": "^0.17.5",
"@sphereon/oid4vci-client": "file:./../../../Sphereon/sphereon-oidvci-client-0.8.1",
"@sphereon/oid4vci-common": "file:./../../../Sphereon/sphereon-oid4vci-common-0.8.1",
"@sphereon/oid4vci-issuer-server": "file:./../../../Sphereon/sphereon-oid4vci-issuer-server-0.8.1",
"@sphereon/oid4vci-issuer": "file:./../../../Sphereon/sphereon-oid4vci-issuer-0.8.1",
"@sphereon/did-auth-siop": "^0.5.0-unstable.8",
"@sphereon/pex": "2.2.0",
"@sphereon/pex-models": "^2.1.1",
"@sphereon/ssi-types": "^0.17.6-unstable.69",
"@sphereon/oid4vci-client": "*",
"@sphereon/oid4vci-common": "*",
"@sphereon/oid4vci-issuer-server": "*",
"@sphereon/oid4vci-issuer": "*",
"@sphereon/did-auth-siop": "*",
"@sphereon/pex": "*",
"@sphereon/pex-models": "^2.1.2",
"body-parser": "^1.20.2",
"jsonpath": "^1.1.1"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ const universityDegreeCredentialLd: CredentialSupported & { id: string } = {
const universityDegreeCredentialSdJwt = {
id: 'https://openid4vc-issuer.com/credentials/UniversityDegreeCredentialSdJwt',
format: OpenIdCredentialFormatProfile.SdJwtVc,
credential_definition: {
vct: 'UniversityDegreeCredential',
},
vct: 'UniversityDegreeCredential',
} satisfies CredentialSupported & { id: string }

const baseCredentialRequestOptions = {
Expand Down Expand Up @@ -614,7 +612,7 @@ describe('OpenId4VcHolder', () => {
it('e2e flow with issuer endpoints requesting multiple credentials', async () => {
const router = Router()
await issuer.modules.openId4VcIssuer.configureRouter(router, {
basePath: '/',
baseUrl: '/',
metadataEndpointConfig: { enabled: true },
accessTokenEndpointConfig: {
enabled: true,
Expand Down Expand Up @@ -663,7 +661,7 @@ describe('OpenId4VcHolder', () => {
userPinRequired: false,
}

const { credentialOfferRequest } = await issuer.modules.openId4VcIssuer.createCredentialOfferAndRequest(
const { credentialOfferUri } = await issuer.modules.openId4VcIssuer.createCredentialOffer(
[
openBadgeCredential.id,
{
Expand All @@ -678,9 +676,7 @@ describe('OpenId4VcHolder', () => {
}
)

const resolvedCredentialOffer = await holder.modules.openId4VcHolder.resolveCredentialOffer(
credentialOfferRequest
)
const resolvedCredentialOffer = await holder.modules.openId4VcHolder.resolveCredentialOffer(credentialOfferUri)

const credentials = await holder.modules.openId4VcHolder.acceptCredentialOfferUsingPreAuthorizedCode(
resolvedCredentialOffer,
Expand Down Expand Up @@ -712,7 +708,7 @@ describe('OpenId4VcHolder', () => {
it('e2e flow with issuer endpoints requesting sdjwtvc', async () => {
const router = Router()
await issuer.modules.openId4VcIssuer.configureRouter(router, {
basePath: '/',
baseUrl: '/',
metadataEndpointConfig: { enabled: true },
accessTokenEndpointConfig: {
enabled: true,
Expand Down Expand Up @@ -744,14 +740,12 @@ describe('OpenId4VcHolder', () => {
issuerApp.use('/', router)
issuerServer = issuerApp.listen(issuerPort)

const { credentialOfferRequest } = await issuer.modules.openId4VcIssuer.createCredentialOfferAndRequest(
const { credentialOfferUri } = await issuer.modules.openId4VcIssuer.createCredentialOffer(
[universityDegreeCredentialSdJwt.id],
{ preAuthorizedCodeFlowConfig: { userPinRequired: false }, ...baseCredentialRequestOptions }
)

const resolvedCredentialOffer = await holder.modules.openId4VcHolder.resolveCredentialOffer(
credentialOfferRequest
)
const resolvedCredentialOffer = await holder.modules.openId4VcHolder.resolveCredentialOffer(credentialOfferUri)

const credentials = await holder.modules.openId4VcHolder.acceptCredentialOfferUsingPreAuthorizedCode(
resolvedCredentialOffer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export function getOfferedCredentialsWithMetadata(
offerType: OfferedCredentialType.CredentialSupported,
credentialSupported: foundSupportedCredential,
format: OpenIdCredentialFormatProfile.SdJwtVc,
types: [foundSupportedCredential.credential_definition.vct],
types: [foundSupportedCredential.vct],
})
} else {
offeredCredentialsWithMetadata.push({
Expand All @@ -130,7 +130,7 @@ export function getOfferedCredentialsWithMetadata(
} else if (offeredCredential.format === 'jwt_vc_json-ld' || offeredCredential.format === 'ldp_vc') {
types = offeredCredential.credential_definition.types
} else if (offeredCredential.format === 'vc+sd-jwt') {
types = [offeredCredential.credential_definition.vct]
types = [offeredCredential.vct]
} else {
throw new AriesFrameworkError(`Unknown format received ${JSON.stringify(offeredCredential.format)}`)
}
Expand Down
Loading

0 comments on commit b77a188

Please sign in to comment.