Skip to content

Commit

Permalink
feat: credentials supported to configurations
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <[email protected]>
  • Loading branch information
TimoGlastra committed Nov 16, 2024
1 parent 14141a5 commit 0bcd792
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 6 deletions.
2 changes: 1 addition & 1 deletion packages/oauth2/src/access-token/create-access-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface CreateAccessTokenOptions {
dpopJwk?: Jwk

/**
* scope of the access token. If the authorization reqeust included scopes
* scope of the access token. If the authorization request included scopes
* they should be added to the access token as well
*/
scope?: string
Expand Down
2 changes: 1 addition & 1 deletion packages/oid4vci/src/Oid4vciClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export class Oid4vciClient {
authorizationServer: authorizationServerMetadata.issuer,
}
} catch (error) {
// Authorization server asks us to complete oid4vp reqeust before issuance
// Authorization server asks us to complete oid4vp request before issuance
if (
error instanceof Oauth2ClientAuthorizationChallengeError &&
error.errorResponse.error === Oauth2ErrorCodes.InsufficientAuthorization &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ async function retrieveCredentials(
const { batch_credential_issuance } = options.issuerMetadata.credentialIssuer
if (!batch_credential_issuance) {
throw new Oauth2Error(
`Credential issuer '${options.issuerMetadata.credentialIssuer}' does not support batch credential issuance using the 'proofs' request property. Only 'proof' is supported.`
`Credential issuer '${options.issuerMetadata.credentialIssuer.credential_issuer}' does not support batch credential issuance using the 'proofs' request property. Only 'proof' is supported.`
)
}

const proofs = Object.values(credentialRequest.proofs)[0]
if (proofs.length > batch_credential_issuance.batch_size) {
throw new Oauth2Error(
`Credential issuer '${options.issuerMetadata.credentialIssuer}' supports batch issuance, but the max batch size is '${batch_credential_issuance.batch_size}'. A total of '${proofs.length}' proofs were provided.`
`Credential issuer '${options.issuerMetadata.credentialIssuer.credential_issuer}' supports batch issuance, but the max batch size is '${batch_credential_issuance.batch_size}'. A total of '${proofs.length}' proofs were provided.`
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class Oid4vciRetrieveCredentialsError extends Oid4vciError {
responseText: string
) {
super(
`${message}\n${JSON.stringify(response.credentialResponseResult ?? response.credentialErrorResponseResult ?? responseText, null, 2)}`
`${message}\n${JSON.stringify(response.credentialResponseResult?.output ?? response.credentialErrorResponseResult?.output ?? responseText, null, 2)}`
)
}
}
1 change: 1 addition & 0 deletions packages/oid4vci/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export type {
export {
extractScopesForCredentialConfigurationIds,
type ExtractScopesForCredentialConfigurationIdsOptions,
credentialsSupportedToCredentialConfigurationsSupported,
} from './metadata/credential-issuer/credential-configurations'

export { Oid4vciClient, type Oid4vciClientOptions, AuthorizationFlow } from './Oid4vciClient'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { describe, expect, test } from 'vitest'
import { credentialsSupportedToCredentialConfigurationsSupported } from '../credential-configurations'

describe('Credential Configurations', () => {
test('credentials supported to credential configurations supported', () => {
expect(
credentialsSupportedToCredentialConfigurationsSupported([
{
id: 'd2662472-891c-413d-b3c6-e2f0109001c5',
format: 'ldp_vc',
'@context': [],
types: ['VerifiableCredential', 'OpenBadgeCredential'],
cryptographic_binding_methods_supported: ['did:key'],
cryptographic_suites_supported: ['Ed25519Signature2018'],
display: [
{
name: 'Example University Degree',
description: 'JFF Plugfest 3 OpenBadge Credential',
background_color: '#464c49',
logo: {},
},
],
},
{
id: '613ecbbb-0a4c-4041-bb78-c64943139d5f',
format: 'jwt_vc_json',
types: ['VerifiableCredential', 'OpenBadgeCredential'],
cryptographic_binding_methods_supported: ['did:key'],
cryptographic_suites_supported: ['EdDSA'],
display: [
{
name: 'Example University Degree',
description: 'JFF Plugfest 3 OpenBadge Credential',
background_color: '#464c49',
logo: {},
},
],
},
{
id: '904afaa1-f319-4a12-9c3c-0a6081c3feb0',
format: 'mso_mdoc',
doctype: 'some-doc-type',
cryptographic_binding_methods_supported: ['did:key'],
cryptographic_suites_supported: ['EdDSA'],
display: [
{
name: 'Passport',
description: 'Passport of the Kingdom of Kākāpō',
background_color: '#171717',
logo: {},
},
],
},
{
id: 'c3db5513-ae2b-46e9-8a0d-fbfd0ce52b6a',
format: 'vc+sd-jwt',
vct: 'something',
cryptographic_binding_methods_supported: ['did:key'],
cryptographic_suites_supported: ['EdDSA'],
display: [
{
name: 'Passport',
description: 'Passport of the Kingdom of Kākāpō',
background_color: '#171717',
logo: { url: 'https://static.mattr.global/credential-assets/government-of-kakapo/web/logo.svg' },
},
],
},
])
).toEqual({
'd2662472-891c-413d-b3c6-e2f0109001c5': {
format: 'ldp_vc',
credential_definition: {
'@context': [],
type: ['VerifiableCredential', 'OpenBadgeCredential'],
},
cryptographic_binding_methods_supported: ['did:key'],
credential_signing_alg_values_supported: ['Ed25519Signature2018'],
display: [
{
name: 'Example University Degree',
description: 'JFF Plugfest 3 OpenBadge Credential',
background_color: '#464c49',
},
],
},
'613ecbbb-0a4c-4041-bb78-c64943139d5f': {
format: 'jwt_vc_json',
credential_definition: {
type: ['VerifiableCredential', 'OpenBadgeCredential'],
},
cryptographic_binding_methods_supported: ['did:key'],
credential_signing_alg_values_supported: ['EdDSA'],
display: [
{
name: 'Example University Degree',
description: 'JFF Plugfest 3 OpenBadge Credential',
background_color: '#464c49',
},
],
},
'904afaa1-f319-4a12-9c3c-0a6081c3feb0': {
format: 'mso_mdoc',
doctype: 'some-doc-type',
cryptographic_binding_methods_supported: ['did:key'],
credential_signing_alg_values_supported: ['EdDSA'],
display: [
{
name: 'Passport',
description: 'Passport of the Kingdom of Kākāpō',
background_color: '#171717',
},
],
},
'c3db5513-ae2b-46e9-8a0d-fbfd0ce52b6a': {
format: 'vc+sd-jwt',
vct: 'something',
cryptographic_binding_methods_supported: ['did:key'],
credential_signing_alg_values_supported: ['EdDSA'],
display: [
{
name: 'Passport',
description: 'Passport of the Kingdom of Kākāpō',
background_color: '#171717',
logo: { uri: 'https://static.mattr.global/credential-assets/government-of-kakapo/web/logo.svg' },
},
],
},
})
})
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { Oauth2Error } from '@animo-id/oauth2'
import { ValidationError } from '@animo-id/oauth2-utils'
import * as v from 'valibot'
import { Oid4vciError } from '../../error/Oid4vciError'
import type { IssuerMetadataResult } from '../fetch-issuer-metadata'
import {
type CredentialConfigurationsSupported,
vCredentialConfigurationSupportedDraft11To14,
} from './v-credential-issuer-metadata'

export interface ExtractScopesForCredentialConfigurationIdsOptions {
/**
Expand Down Expand Up @@ -47,3 +54,36 @@ export function extractScopesForCredentialConfigurationIds(

return scopes.size > 0 ? Array.from(scopes) : undefined
}

/**
* Transforms draft 11 credentials supported syntax to credential configurations supported
*
* @throws if a credentials supported entry without id is passed
* @throws if a credentials supported entry with invalid structure or format specific properties is passed
*/
export function credentialsSupportedToCredentialConfigurationsSupported(
credentialsSupported: Array<v.InferInput<typeof vCredentialConfigurationSupportedDraft11To14>>
) {
const credentialConfigurationsSupported: CredentialConfigurationsSupported = {}

for (let index = 0; index < credentialsSupported.length; index++) {
const credentialSupported = credentialsSupported[index]
if (!credentialSupported.id) {
throw new Oid4vciError(
`Credential supported at index '${index}' does not have an 'id' property. Credential configuration requires the 'id' property as key`
)
}

const parseResult = v.safeParse(vCredentialConfigurationSupportedDraft11To14, credentialSupported)
if (!parseResult.success) {
throw new ValidationError(
`Error transforming credential supported with id '${credentialSupported.id}' to credential configuration supported format`,
parseResult.issues
)
}

credentialConfigurationsSupported[credentialSupported.id] = parseResult.output
}

return credentialConfigurationsSupported
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const vCredentialIssuerMetadataDraft14 = v.looseObject({

// Transforms credential supported to credential configuration supported format
// Ignores unknown formats
const vCredentialConfigurationSupportedDraft11To14 = v.pipe(
export const vCredentialConfigurationSupportedDraft11To14 = v.pipe(
v.looseObject({
id: v.optional(v.string()),
format: v.string(),
Expand Down

0 comments on commit 0bcd792

Please sign in to comment.