@sphereon/ssi-sdk.oid4vci-holder 0.32.0 → 0.32.1-feature.MWALL.715.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,8 @@
1
1
  import { LOG } from '@sphereon/oid4vci-client'
2
2
  import {
3
3
  CredentialConfigurationSupported,
4
+ CredentialSupportedSdJwtVc,
5
+ CredentialConfigurationSupportedSdJwtVcV1_0_13,
4
6
  CredentialOfferFormatV1_0_11,
5
7
  CredentialResponse,
6
8
  getSupportedCredentials,
@@ -30,6 +32,7 @@ import {
30
32
  OriginalVerifiableCredential,
31
33
  sdJwtDecodedCredentialToUniformCredential,
32
34
  SdJwtDecodedVerifiableCredential,
35
+ SdJwtTypeMetadata,
33
36
  W3CVerifiableCredential,
34
37
  WrappedVerifiableCredential,
35
38
  } from '@sphereon/ssi-types'
@@ -54,31 +57,47 @@ import {
54
57
  VerificationResult,
55
58
  VerifyCredentialToAcceptArgs,
56
59
  } from '../types/IOID4VCIHolder'
57
- import { getCredentialBrandingFrom, issuerLocaleBrandingFrom } from './OIDC4VCIBrandingMapper'
60
+ import { oid4vciGetCredentialBrandingFrom, sdJwtGetCredentialBrandingFrom, issuerLocaleBrandingFrom } from './OIDC4VCIBrandingMapper'
58
61
 
59
62
  export const getCredentialBranding = async (args: GetCredentialBrandingArgs): Promise<Record<string, Array<IBasicCredentialLocaleBranding>>> => {
60
63
  const { credentialsSupported, context } = args
61
64
  const credentialBranding: Record<string, Array<IBasicCredentialLocaleBranding>> = {}
62
65
  await Promise.all(
63
- Object.entries(credentialsSupported).map(async ([configId, credentialsConfigSupported]) => {
64
- const mappedLocaleBranding = await getCredentialBrandingFrom({
65
- credentialDisplay: credentialsConfigSupported.display,
66
- issuerCredentialSubject:
67
- // @ts-ignore // FIXME SPRIND-123 add proper support for type recognition as claim display can be located elsewhere for v13
68
- credentialsSupported.claims !== undefined ? credentialsConfigSupported.claims : credentialsConfigSupported.credentialSubject,
69
- })
70
-
66
+ Object.entries(credentialsSupported).map(async ([configId, credentialsConfigSupported]): Promise<void> => {
67
+ let sdJwtTypeMetadata: SdJwtTypeMetadata | undefined
68
+ if (credentialsConfigSupported.format === 'vc+sd-jwt') {
69
+ const vct = (<CredentialSupportedSdJwtVc | CredentialConfigurationSupportedSdJwtVcV1_0_13>credentialsConfigSupported).vct
70
+ if (vct.startsWith('http')) {
71
+ try {
72
+ sdJwtTypeMetadata = await context.agent.fetchSdJwtTypeMetadataFromVctUrl({ vct })
73
+ } catch {
74
+ // For now, we are just going to ignore and continue without any branding as we still have a fallback
75
+ }
76
+ }
77
+ }
78
+ let mappedLocaleBranding: Array<IBasicCredentialLocaleBranding> = []
79
+ if (sdJwtTypeMetadata) {
80
+ mappedLocaleBranding = await sdJwtGetCredentialBrandingFrom({
81
+ credentialDisplay: sdJwtTypeMetadata.display,
82
+ claimsMetadata: sdJwtTypeMetadata.claims,
83
+ })
84
+ } else {
85
+ mappedLocaleBranding = await oid4vciGetCredentialBrandingFrom({
86
+ credentialDisplay: credentialsConfigSupported.display,
87
+ issuerCredentialSubject:
88
+ // @ts-ignore // FIXME SPRIND-123 add proper support for type recognition as claim display can be located elsewhere for v13
89
+ credentialsSupported.claims !== undefined ? credentialsConfigSupported.claims : credentialsConfigSupported.credentialSubject,
90
+ })
91
+ }
71
92
  // TODO we should make the mapper part of the plugin, so that the logic for getting the branding becomes more clear and easier to use
72
93
  const localeBranding = await Promise.all(
73
- (mappedLocaleBranding ?? []).map(
94
+ mappedLocaleBranding.map(
74
95
  async (localeBranding): Promise<IBasicCredentialLocaleBranding> => await context.agent.ibCredentialLocaleBrandingFrom({ localeBranding }),
75
96
  ),
76
97
  )
77
-
78
98
  const defaultCredentialType = 'VerifiableCredential'
79
99
  const configSupportedTypes = getTypesFromCredentialSupported(credentialsConfigSupported)
80
100
  const credentialTypes: Array<string> = configSupportedTypes.length === 0 ? asArray(defaultCredentialType) : configSupportedTypes
81
-
82
101
  const filteredCredentialTypes = credentialTypes.filter((type: string): boolean => type !== defaultCredentialType)
83
102
  credentialBranding[filteredCredentialTypes[0]] = localeBranding // TODO for now taking the first type
84
103
  }),
@@ -1,16 +1,79 @@
1
1
  import { CredentialsSupportedDisplay, NameAndLocale } from '@sphereon/oid4vci-common'
2
2
  import { IBasicCredentialClaim, IBasicCredentialLocaleBranding, IBasicIssuerLocaleBranding } from '@sphereon/ssi-sdk.data-store'
3
+ import { SdJwtClaimDisplayMetadata, SdJwtClaimMetadata, SdJwtClaimPath, SdJwtTypeDisplayMetadata } from '@sphereon/ssi-types'
3
4
  import {
4
- CredentialLocaleBrandingFromArgs,
5
5
  IssuerLocaleBrandingFromArgs,
6
- CredentialBrandingFromArgs,
7
- CredentialDisplayLocalesFromArgs,
8
- IssuerCredentialSubjectLocalesFromArgs,
9
- CombineLocalesFromArgs,
6
+ Oid4vciCombineDisplayLocalesFromArgs,
7
+ Oid4vciCredentialDisplayLocalesFromArgs,
8
+ Oid4vciCredentialLocaleBrandingFromArgs,
9
+ Oid4vciGetCredentialBrandingFromArgs,
10
+ Oid4vciIssuerCredentialSubjectLocalesFromArgs,
11
+ SdJwtCombineDisplayLocalesFromArgs,
12
+ SdJwtCredentialClaimLocalesFromArgs,
13
+ SdJwtCredentialDisplayLocalesFromArgs,
14
+ SdJwtCredentialLocaleBrandingFromArgs,
15
+ SdJwtGetCredentialBrandingFromArgs,
10
16
  } from '../types/IOID4VCIHolder'
11
17
 
12
18
  // FIXME should we not move this to the branding plugin?
13
- export const credentialLocaleBrandingFrom = async (args: CredentialLocaleBrandingFromArgs): Promise<IBasicCredentialLocaleBranding> => {
19
+
20
+ export const oid4vciGetCredentialBrandingFrom = async (
21
+ args: Oid4vciGetCredentialBrandingFromArgs,
22
+ ): Promise<Array<IBasicCredentialLocaleBranding>> => {
23
+ const { credentialDisplay, issuerCredentialSubject } = args
24
+
25
+ return oid4vciCombineDisplayLocalesFrom({
26
+ ...(issuerCredentialSubject && { issuerCredentialSubjectLocales: await oid4vciIssuerCredentialSubjectLocalesFrom({ issuerCredentialSubject }) }),
27
+ ...(credentialDisplay && { credentialDisplayLocales: await oid4vciCredentialDisplayLocalesFrom({ credentialDisplay }) }),
28
+ })
29
+ }
30
+
31
+ export const oid4vciCredentialDisplayLocalesFrom = async (
32
+ args: Oid4vciCredentialDisplayLocalesFromArgs,
33
+ ): Promise<Map<string, CredentialsSupportedDisplay>> => {
34
+ const { credentialDisplay } = args
35
+ return credentialDisplay.reduce((localeDisplays, display) => {
36
+ const localeKey = display.locale || ''
37
+ localeDisplays.set(localeKey, display)
38
+ return localeDisplays
39
+ }, new Map<string, CredentialsSupportedDisplay>())
40
+ }
41
+
42
+ export const oid4vciIssuerCredentialSubjectLocalesFrom = async (
43
+ args: Oid4vciIssuerCredentialSubjectLocalesFromArgs,
44
+ ): Promise<Map<string, Array<IBasicCredentialClaim>>> => {
45
+ const { issuerCredentialSubject } = args
46
+ const localeClaims = new Map<string, Array<IBasicCredentialClaim>>()
47
+
48
+ const processClaimObject = (claim: any, parentKey: string = ''): void => {
49
+ Object.entries(claim).forEach(([key, value]): void => {
50
+ if (key === 'mandatory' || key === 'value_type') {
51
+ return
52
+ }
53
+
54
+ if (key === 'display' && Array.isArray(value)) {
55
+ value.forEach(({ name, locale = '' }: NameAndLocale): void => {
56
+ if (!name) {
57
+ return
58
+ }
59
+
60
+ //const localeKey = locale || ''
61
+ if (!localeClaims.has(locale)) {
62
+ localeClaims.set(locale, [])
63
+ }
64
+ localeClaims.get(locale)!.push({ key: parentKey, name })
65
+ })
66
+ } else if (typeof value === 'object' && value !== null) {
67
+ processClaimObject(value, parentKey ? `${parentKey}.${key}` : key)
68
+ }
69
+ })
70
+ }
71
+
72
+ processClaimObject(issuerCredentialSubject)
73
+ return localeClaims
74
+ }
75
+
76
+ export const oid4vciCredentialLocaleBrandingFrom = async (args: Oid4vciCredentialLocaleBrandingFromArgs): Promise<IBasicCredentialLocaleBranding> => {
14
77
  const { credentialDisplay } = args
15
78
 
16
79
  return {
@@ -59,6 +122,126 @@ export const credentialLocaleBrandingFrom = async (args: CredentialLocaleBrandin
59
122
  }
60
123
  }
61
124
 
125
+ export const oid4vciCombineDisplayLocalesFrom = async (
126
+ args: Oid4vciCombineDisplayLocalesFromArgs,
127
+ ): Promise<Array<IBasicCredentialLocaleBranding>> => {
128
+ const {
129
+ credentialDisplayLocales = new Map<string, CredentialsSupportedDisplay>(),
130
+ issuerCredentialSubjectLocales = new Map<string, Array<IBasicCredentialClaim>>(),
131
+ } = args
132
+
133
+ const locales: Array<string> = Array.from(new Set([...issuerCredentialSubjectLocales.keys(), ...credentialDisplayLocales.keys()]))
134
+
135
+ return Promise.all(
136
+ locales.map(async (locale: string): Promise<IBasicCredentialLocaleBranding> => {
137
+ const display = credentialDisplayLocales.get(locale)
138
+ const claims = issuerCredentialSubjectLocales.get(locale)
139
+
140
+ return {
141
+ ...(display && (await oid4vciCredentialLocaleBrandingFrom({ credentialDisplay: display }))),
142
+ ...(locale.length > 0 && { locale }),
143
+ claims,
144
+ }
145
+ }),
146
+ )
147
+ }
148
+
149
+ export const sdJwtGetCredentialBrandingFrom = async (args: SdJwtGetCredentialBrandingFromArgs): Promise<Array<IBasicCredentialLocaleBranding>> => {
150
+ const { credentialDisplay, claimsMetadata } = args
151
+
152
+ return sdJwtCombineDisplayLocalesFrom({
153
+ ...(claimsMetadata && { claimsMetadata: await sdJwtCredentialClaimLocalesFrom({ claimsMetadata }) }),
154
+ ...(credentialDisplay && { credentialDisplayLocales: await sdJwtCredentialDisplayLocalesFrom({ credentialDisplay }) }),
155
+ })
156
+ }
157
+
158
+ export const sdJwtCredentialDisplayLocalesFrom = async (
159
+ args: SdJwtCredentialDisplayLocalesFromArgs,
160
+ ): Promise<Map<string, SdJwtTypeDisplayMetadata>> => {
161
+ const { credentialDisplay } = args
162
+ return credentialDisplay.reduce((localeDisplays, display) => {
163
+ const localeKey = display.lang || ''
164
+ localeDisplays.set(localeKey, display)
165
+ return localeDisplays
166
+ }, new Map<string, SdJwtTypeDisplayMetadata>())
167
+ }
168
+
169
+ export const sdJwtCredentialClaimLocalesFrom = async (
170
+ args: SdJwtCredentialClaimLocalesFromArgs,
171
+ ): Promise<Map<string, Array<IBasicCredentialClaim>>> => {
172
+ const { claimsMetadata } = args
173
+ const localeClaims = new Map<string, Array<IBasicCredentialClaim>>()
174
+
175
+ claimsMetadata.forEach((claim: SdJwtClaimMetadata): void => {
176
+ claim.display?.forEach((display: SdJwtClaimDisplayMetadata): void => {
177
+ const { lang = '', label } = display
178
+ const key = claim.path.map((value: SdJwtClaimPath) => String(value)).join('.')
179
+ if (!localeClaims.has(lang)) {
180
+ localeClaims.set(lang, [])
181
+ }
182
+ localeClaims.get(lang)!.push({ key, name: label })
183
+ })
184
+ })
185
+
186
+ return localeClaims
187
+ }
188
+
189
+ export const sdJwtCredentialLocaleBrandingFrom = async (args: SdJwtCredentialLocaleBrandingFromArgs): Promise<IBasicCredentialLocaleBranding> => {
190
+ const { credentialDisplay } = args
191
+
192
+ return {
193
+ ...(credentialDisplay.name && {
194
+ alias: credentialDisplay.name,
195
+ }),
196
+ ...(credentialDisplay.lang && {
197
+ locale: credentialDisplay.lang,
198
+ }),
199
+ ...(credentialDisplay.rendering?.simple?.logo && {
200
+ logo: {
201
+ ...(credentialDisplay.rendering.simple.logo.uri && {
202
+ uri: credentialDisplay.rendering.simple.logo.uri,
203
+ }),
204
+ ...(credentialDisplay.rendering.simple.logo.alt_text && {
205
+ alt: credentialDisplay.rendering.simple.logo.alt_text,
206
+ }),
207
+ },
208
+ }),
209
+ ...(credentialDisplay.description && {
210
+ description: credentialDisplay.description,
211
+ }),
212
+ ...(credentialDisplay.rendering?.simple?.text_color && {
213
+ text: {
214
+ color: credentialDisplay.rendering.simple.text_color,
215
+ },
216
+ }),
217
+ ...(credentialDisplay.rendering?.simple?.background_color && {
218
+ background: {
219
+ color: credentialDisplay.rendering.simple.background_color,
220
+ },
221
+ }),
222
+ }
223
+ }
224
+
225
+ export const sdJwtCombineDisplayLocalesFrom = async (args: SdJwtCombineDisplayLocalesFromArgs): Promise<Array<IBasicCredentialLocaleBranding>> => {
226
+ const { credentialDisplayLocales = new Map<string, SdJwtTypeDisplayMetadata>(), claimsMetadata = new Map<string, Array<IBasicCredentialClaim>>() } =
227
+ args
228
+
229
+ const locales: Array<string> = Array.from(new Set([...claimsMetadata.keys(), ...credentialDisplayLocales.keys()]))
230
+
231
+ return Promise.all(
232
+ locales.map(async (locale: string): Promise<IBasicCredentialLocaleBranding> => {
233
+ const display = credentialDisplayLocales.get(locale)
234
+ const claims = claimsMetadata.get(locale)
235
+
236
+ return {
237
+ ...(display && (await sdJwtCredentialLocaleBrandingFrom({ credentialDisplay: display }))),
238
+ ...(locale.length > 0 && { locale }),
239
+ claims,
240
+ }
241
+ }),
242
+ )
243
+ }
244
+
62
245
  // TODO since dynamicRegistrationClientMetadata can also be on a RP, we should start using this mapper in a more general way
63
246
  export const issuerLocaleBrandingFrom = async (args: IssuerLocaleBrandingFromArgs): Promise<IBasicIssuerLocaleBranding> => {
64
247
  const { issuerDisplay, dynamicRegistrationClientMetadata } = args
@@ -108,77 +291,3 @@ export const issuerLocaleBrandingFrom = async (args: IssuerLocaleBrandingFromArg
108
291
  }),
109
292
  }
110
293
  }
111
-
112
- export const getCredentialBrandingFrom = async (args: CredentialBrandingFromArgs): Promise<Array<IBasicCredentialLocaleBranding>> => {
113
- const { credentialDisplay, issuerCredentialSubject } = args
114
-
115
- return combineDisplayLocalesFrom({
116
- ...(issuerCredentialSubject && { issuerCredentialSubjectLocales: await issuerCredentialSubjectLocalesFrom({ issuerCredentialSubject }) }),
117
- ...(credentialDisplay && { credentialDisplayLocales: await credentialDisplayLocalesFrom({ credentialDisplay }) }),
118
- })
119
- }
120
-
121
- const credentialDisplayLocalesFrom = async (args: CredentialDisplayLocalesFromArgs): Promise<Map<string, CredentialsSupportedDisplay>> => {
122
- const { credentialDisplay } = args
123
- return credentialDisplay.reduce((localeDisplays, display) => {
124
- const localeKey = display.locale || ''
125
- localeDisplays.set(localeKey, display)
126
- return localeDisplays
127
- }, new Map<string, CredentialsSupportedDisplay>())
128
- }
129
-
130
- const issuerCredentialSubjectLocalesFrom = async (
131
- args: IssuerCredentialSubjectLocalesFromArgs,
132
- ): Promise<Map<string, Array<IBasicCredentialClaim>>> => {
133
- const { issuerCredentialSubject } = args
134
- const localeClaims = new Map<string, Array<IBasicCredentialClaim>>()
135
-
136
- const processClaimObject = (claim: any, parentKey: string = ''): void => {
137
- Object.entries(claim).forEach(([key, value]): void => {
138
- if (key === 'mandatory' || key === 'value_type') {
139
- return
140
- }
141
-
142
- if (key === 'display' && Array.isArray(value)) {
143
- value.forEach(({ name, locale }: NameAndLocale): void => {
144
- if (!name) {
145
- return
146
- }
147
-
148
- const localeKey = locale || ''
149
- if (!localeClaims.has(localeKey)) {
150
- localeClaims.set(localeKey, [])
151
- }
152
- localeClaims.get(localeKey)!.push({ key: parentKey, name })
153
- })
154
- } else if (typeof value === 'object' && value !== null) {
155
- processClaimObject(value, parentKey ? `${parentKey}.${key}` : key)
156
- }
157
- })
158
- }
159
-
160
- processClaimObject(issuerCredentialSubject)
161
- return localeClaims
162
- }
163
-
164
- const combineDisplayLocalesFrom = async (args: CombineLocalesFromArgs): Promise<Array<IBasicCredentialLocaleBranding>> => {
165
- const {
166
- credentialDisplayLocales = new Map<string, CredentialsSupportedDisplay>(),
167
- issuerCredentialSubjectLocales = new Map<string, Array<IBasicCredentialClaim>>(),
168
- } = args
169
-
170
- const locales: Array<string> = Array.from(new Set([...issuerCredentialSubjectLocales.keys(), ...credentialDisplayLocales.keys()]))
171
-
172
- return Promise.all(
173
- locales.map(async (locale: string): Promise<IBasicCredentialLocaleBranding> => {
174
- const display = credentialDisplayLocales.get(locale)
175
- const claims = issuerCredentialSubjectLocales.get(locale)
176
-
177
- return {
178
- ...(display && (await credentialLocaleBrandingFrom({ credentialDisplay: display }))),
179
- ...(locale.length > 0 && { locale }),
180
- claims,
181
- }
182
- }),
183
- )
184
- }
@@ -43,6 +43,8 @@ import {
43
43
  JoseSignatureAlgorithm,
44
44
  JoseSignatureAlgorithmString,
45
45
  OriginalVerifiableCredential,
46
+ SdJwtTypeDisplayMetadata,
47
+ SdJwtClaimMetadata,
46
48
  W3CVerifiableCredential,
47
49
  WrappedVerifiableCredential,
48
50
  WrappedVerifiablePresentation,
@@ -667,33 +669,55 @@ export type VerifyEBSICredentialIssuerResult = {
667
669
  attributes: Attribute[]
668
670
  }
669
671
 
670
- export type CredentialLocaleBrandingFromArgs = {
672
+ export type Oid4vciCredentialLocaleBrandingFromArgs = {
671
673
  credentialDisplay: CredentialsSupportedDisplay
672
674
  }
673
675
 
676
+ export type SdJwtCredentialLocaleBrandingFromArgs = {
677
+ credentialDisplay: SdJwtTypeDisplayMetadata
678
+ }
679
+
680
+ export type SdJwtGetCredentialBrandingFromArgs = {
681
+ credentialDisplay?: Array<SdJwtTypeDisplayMetadata>
682
+ claimsMetadata?: Array<SdJwtClaimMetadata>
683
+ }
684
+
685
+ export type SdJwtCredentialClaimLocalesFromArgs = {
686
+ claimsMetadata: Array<SdJwtClaimMetadata>
687
+ }
688
+
674
689
  export type IssuerLocaleBrandingFromArgs = {
675
690
  issuerDisplay: MetadataDisplay
676
691
  dynamicRegistrationClientMetadata?: DynamicRegistrationClientMetadataDisplay
677
692
  }
678
693
 
679
- export type CredentialBrandingFromArgs = {
694
+ export type Oid4vciGetCredentialBrandingFromArgs = {
680
695
  credentialDisplay?: Array<CredentialsSupportedDisplay>
681
696
  issuerCredentialSubject?: IssuerCredentialSubject
682
697
  }
683
698
 
684
- export type CredentialDisplayLocalesFromArgs = {
699
+ export type Oid4vciCredentialDisplayLocalesFromArgs = {
685
700
  credentialDisplay: Array<CredentialsSupportedDisplay>
686
701
  }
687
702
 
688
- export type IssuerCredentialSubjectLocalesFromArgs = {
703
+ export type SdJwtCredentialDisplayLocalesFromArgs = {
704
+ credentialDisplay: Array<SdJwtTypeDisplayMetadata>
705
+ }
706
+
707
+ export type Oid4vciIssuerCredentialSubjectLocalesFromArgs = {
689
708
  issuerCredentialSubject: IssuerCredentialSubject
690
709
  }
691
710
 
692
- export type CombineLocalesFromArgs = {
711
+ export type Oid4vciCombineDisplayLocalesFromArgs = {
693
712
  credentialDisplayLocales?: Map<string, CredentialsSupportedDisplay>
694
713
  issuerCredentialSubjectLocales?: Map<string, Array<IBasicCredentialClaim>>
695
714
  }
696
715
 
716
+ export type SdJwtCombineDisplayLocalesFromArgs = {
717
+ credentialDisplayLocales?: Map<string, SdJwtTypeDisplayMetadata>
718
+ claimsMetadata?: Map<string, Array<IBasicCredentialClaim>>
719
+ }
720
+
697
721
  export type DynamicRegistrationClientMetadataDisplay = Pick<
698
722
  DynamicRegistrationClientMetadata,
699
723
  'client_name' | 'client_uri' | 'contacts' | 'tos_uri' | 'policy_uri' | 'logo_uri'