@@ -16,9 +16,14 @@ import {
1616 DifPresentationExchangeSubmissionLocation ,
1717 Hasher ,
1818 injectable ,
19+ JwsService ,
1920 Kms ,
2021 TypedArrayEncoder ,
2122} from '@credo-ts/core'
23+ import {
24+ fetchEntityConfiguration as federationFetchEntityConfiguration ,
25+ resolveTrustChains as federationResolveTrustChains ,
26+ } from '@openid-federation/core'
2227import type { Jwk } from '@openid4vc/oauth2'
2328import {
2429 extractEncryptionJwkFromJwks ,
@@ -35,7 +40,9 @@ import type { OpenId4VpVersion } from '../openid4vc-verifier'
3540import { getOid4vcCallbacks } from '../shared/callbacks'
3641import type {
3742 OpenId4VpAcceptAuthorizationRequestOptions ,
43+ OpenId4VpFetchEntityConfigurationOptions ,
3844 OpenId4VpResolvedAuthorizationRequest ,
45+ OpenId4VpResolveTrustChainsOptions ,
3946 ParsedTransactionDataEntry ,
4047 ResolveOpenId4VpAuthorizationRequestOptions ,
4148} from './OpenId4vpHolderServiceOptions'
@@ -49,10 +56,15 @@ export class OpenId4VpHolderService {
4956
5057 private getOpenid4vpClient (
5158 agentContext : AgentContext ,
52- options ?: { trustedCertificates ?: EncodedX509Certificate [ ] ; isVerifyOpenId4VpAuthorizationRequest ?: boolean }
59+ options ?: {
60+ trustedCertificates ?: EncodedX509Certificate [ ]
61+ trustedFederationEntityIds ?: string [ ]
62+ isVerifyOpenId4VpAuthorizationRequest ?: boolean
63+ }
5364 ) {
5465 const callbacks = getOid4vcCallbacks ( agentContext , {
5566 trustedCertificates : options ?. trustedCertificates ,
67+ trustedFederationEntityIds : options ?. trustedFederationEntityIds ,
5668 isVerifyOpenId4VpAuthorizationRequest : options ?. isVerifyOpenId4VpAuthorizationRequest ,
5769 } )
5870 return new Openid4vpClient ( { callbacks } )
@@ -121,6 +133,7 @@ export class OpenId4VpHolderService {
121133 ) : Promise < OpenId4VpResolvedAuthorizationRequest > {
122134 const openid4vpClient = this . getOpenid4vpClient ( agentContext , {
123135 trustedCertificates : options ?. trustedCertificates ,
136+ trustedFederationEntityIds : options ?. trustedFederationEntityIds ,
124137 isVerifyOpenId4VpAuthorizationRequest : true ,
125138 } )
126139 const { params } = openid4vpClient . parseOpenid4vpAuthorizationRequest ( { authorizationRequest } )
@@ -138,11 +151,47 @@ export class OpenId4VpHolderService {
138151 client . prefix !== 'x509_hash' &&
139152 client . prefix !== 'decentralized_identifier' &&
140153 client . prefix !== 'origin' &&
141- client . prefix !== 'redirect_uri'
154+ client . prefix !== 'redirect_uri' &&
155+ client . prefix !== 'openid_federation'
142156 ) {
143157 throw new CredoError ( `Client id prefix '${ client . prefix } ' is not supported` )
144158 }
145159
160+ if ( client . prefix === 'openid_federation' ) {
161+ const jwsService = agentContext . dependencyManager . resolve ( JwsService )
162+
163+ const entityConfiguration = await federationFetchEntityConfiguration ( {
164+ entityId : client . identifier ,
165+ verifyJwtCallback : async ( { jwt, jwk } ) => {
166+ const res = await jwsService . verifyJws ( agentContext , {
167+ jws : jwt ,
168+ jwsSigner : {
169+ method : 'jwk' ,
170+ jwk : Kms . PublicJwk . fromUnknown ( jwk ) ,
171+ } ,
172+ } )
173+
174+ return res . isValid
175+ } ,
176+ } )
177+ if ( ! entityConfiguration )
178+ throw new CredoError ( `Unable to fetch entity configuration for entityId '${ client . identifier } '` )
179+
180+ const openidRelyingPartyMetadata = entityConfiguration . metadata ?. openid_relying_party
181+ if ( ! openidRelyingPartyMetadata ) {
182+ throw new CredoError ( `Federation entity '${ client . identifier } ' does not have 'openid_relying_party' metadata.` )
183+ }
184+
185+ // FIXME: we probably don't want to override this, but otherwise the accept logic doesn't have
186+ // access to the correct metadata. Should we also pass client to accept?
187+ // @ts -expect-error
188+ verifiedAuthorizationRequest . authorizationRequestPayload . client_metadata = openidRelyingPartyMetadata
189+ // FIXME: we should not just override the metadata?
190+ // When federation is used we need to use the federation metadata
191+ // @ts -expect-error
192+ client . clientMetadata = openidRelyingPartyMetadata
193+ }
194+
146195 const returnValue = {
147196 authorizationRequestPayload : verifiedAuthorizationRequest . authorizationRequestPayload ,
148197 origin : options ?. origin ,
@@ -169,6 +218,7 @@ export class OpenId4VpHolderService {
169218 verifier : {
170219 clientIdPrefix : client . prefix ,
171220 effectiveClientId : client . effective ,
221+ clientMetadata : client . clientMetadata ,
172222 } ,
173223 transactionData : pexResult ?. matchedTransactionData ?? dcqlResult ?. matchedTransactionData ,
174224 presentationExchange : pexResult ?. pex ,
@@ -504,6 +554,8 @@ export class OpenId4VpHolderService {
504554
505555 const response = await openid4vpClient . createOpenid4vpAuthorizationResponse ( {
506556 authorizationRequestPayload,
557+ // We overwrite the client metadata on the authorization request payload when using OpenID Federation
558+ clientMetadata : authorizationRequestPayload . client_metadata ,
507559 origin : options . origin ,
508560 authorizationResponsePayload : {
509561 vp_token : vpToken ,
@@ -581,4 +633,50 @@ export class OpenId4VpHolderService {
581633 presentationDuringIssuanceSession : responseJson ?. presentation_during_issuance_session as string | undefined ,
582634 } as const
583635 }
636+
637+ public async resolveOpenIdFederationChains ( agentContext : AgentContext , options : OpenId4VpResolveTrustChainsOptions ) {
638+ const jwsService = agentContext . dependencyManager . resolve ( JwsService )
639+
640+ const { entityId, trustAnchorEntityIds } = options
641+
642+ return federationResolveTrustChains ( {
643+ entityId,
644+ trustAnchorEntityIds,
645+ verifyJwtCallback : async ( { jwt, jwk } ) => {
646+ const res = await jwsService . verifyJws ( agentContext , {
647+ jws : jwt ,
648+ jwsSigner : {
649+ method : 'jwk' ,
650+ jwk : Kms . PublicJwk . fromUnknown ( jwk ) ,
651+ } ,
652+ } )
653+
654+ return res . isValid
655+ } ,
656+ } )
657+ }
658+
659+ public async fetchOpenIdFederationEntityConfiguration (
660+ agentContext : AgentContext ,
661+ options : OpenId4VpFetchEntityConfigurationOptions
662+ ) {
663+ const jwsService = agentContext . dependencyManager . resolve ( JwsService )
664+
665+ const { entityId } = options
666+
667+ return federationFetchEntityConfiguration ( {
668+ entityId,
669+ verifyJwtCallback : async ( { jwt, jwk } ) => {
670+ const res = await jwsService . verifyJws ( agentContext , {
671+ jws : jwt ,
672+ jwsSigner : {
673+ method : 'jwk' ,
674+ jwk : Kms . PublicJwk . fromUnknown ( jwk ) ,
675+ } ,
676+ } )
677+
678+ return res . isValid
679+ } ,
680+ } )
681+ }
584682}
0 commit comments