Skip to content

Commit 5f0b11a

Browse files
committed
fix: Search the jwk based on the kid
Signed-off-by: Tom Lanser <tom@devv.nl>
1 parent 8628b5e commit 5f0b11a

File tree

4 files changed

+30
-70
lines changed

4 files changed

+30
-70
lines changed

packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import type {
99
} from './OpenId4VciHolderServiceOptions'
1010
import type {
1111
OpenId4VcSiopAcceptAuthorizationRequestOptions,
12-
OpenId4VcSiopResolveAuthorizationRequestOptions,
1312
OpenId4VcSiopResolveTrustChainsOptions,
13+
OpenId4VcSiopFetchEntityConfigurationOptions,
1414
} from './OpenId4vcSiopHolderServiceOptions'
1515

1616
import { injectable, AgentContext, DifPresentationExchangeService, DifPexCredentialsForRequest } from '@credo-ts/core'
@@ -46,11 +46,8 @@ export class OpenId4VcHolderApi {
4646
* @param requestJwtOrUri JWT or an SIOPv2 request URI
4747
* @returns the resolved and verified authentication request.
4848
*/
49-
public async resolveSiopAuthorizationRequest(
50-
requestJwtOrUri: string,
51-
options: OpenId4VcSiopResolveAuthorizationRequestOptions = {}
52-
) {
53-
return this.openId4VcSiopHolderService.resolveAuthorizationRequest(this.agentContext, requestJwtOrUri, options)
49+
public async resolveSiopAuthorizationRequest(requestJwtOrUri: string) {
50+
return this.openId4VcSiopHolderService.resolveAuthorizationRequest(this.agentContext, requestJwtOrUri)
5451
}
5552

5653
/**
@@ -176,4 +173,8 @@ export class OpenId4VcHolderApi {
176173
public async resolveOpenIdFederationChains(options: OpenId4VcSiopResolveTrustChainsOptions) {
177174
return this.openId4VcSiopHolderService.resolveOpenIdFederationChains(this.agentContext, options)
178175
}
176+
177+
public async fetchOpenIdFederationEntityConfiguration(options: OpenId4VcSiopFetchEntityConfigurationOptions) {
178+
return this.openId4VcSiopHolderService.fetchOpenIdFederationEntityConfiguration(this.agentContext, options)
179+
}
179180
}

packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import type {
22
OpenId4VcSiopAcceptAuthorizationRequestOptions,
33
OpenId4VcSiopFetchEntityConfigurationOptions,
4-
OpenId4VcSiopGetOpenIdProviderOptions,
5-
OpenId4VcSiopResolveAuthorizationRequestOptions,
64
OpenId4VcSiopResolvedAuthorizationRequest,
75
OpenId4VcSiopResolveTrustChainsOptions,
86
} from './OpenId4vcSiopHolderServiceOptions'
@@ -47,12 +45,9 @@ export class OpenId4VcSiopHolderService {
4745

4846
public async resolveAuthorizationRequest(
4947
agentContext: AgentContext,
50-
requestJwtOrUri: string,
51-
options: OpenId4VcSiopResolveAuthorizationRequestOptions = {}
48+
requestJwtOrUri: string
5249
): Promise<OpenId4VcSiopResolvedAuthorizationRequest> {
53-
const openidProvider = await this.getOpenIdProvider(agentContext, {
54-
federation: options.federation,
55-
})
50+
const openidProvider = await this.getOpenIdProvider(agentContext)
5651

5752
// parsing happens automatically in verifyAuthorizationRequest
5853
const verifiedAuthorizationRequest = await openidProvider.verifyAuthorizationRequest(requestJwtOrUri)
@@ -93,10 +88,14 @@ export class OpenId4VcSiopHolderService {
9388
if (!entityConfiguration) throw new CredoError(`Unable to fetch entity configuration for entityId '${clientId}'`)
9489

9590
const openidRelyingPartyMetadata = entityConfiguration.metadata?.openid_relying_party
91+
9692
// When the metadata is present in the federation we want to use that instead of what is passed with the request
9793
if (openidRelyingPartyMetadata) {
9894
verifiedAuthorizationRequest.authorizationRequestPayload.client_metadata = openidRelyingPartyMetadata
95+
verifiedAuthorizationRequest.authorizationRequest.payload.client_metadata = openidRelyingPartyMetadata
9996
}
97+
98+
// TODO: Do we want to do something with the real chain of do we want to give the user the possibility to do that somewhere else with the risk of being forgotten or that it doesn't have enough information at that place?
10099
}
101100

102101
return {
@@ -284,7 +283,7 @@ export class OpenId4VcSiopHolderService {
284283
} as const
285284
}
286285

287-
private async getOpenIdProvider(agentContext: AgentContext, options: OpenId4VcSiopGetOpenIdProviderOptions = {}) {
286+
private async getOpenIdProvider(agentContext: AgentContext) {
288287
const builder = OP.builder()
289288
.withExpiresIn(6000)
290289
.withIssuer(ResponseIss.SELF_ISSUED_V2)
@@ -295,11 +294,7 @@ export class OpenId4VcSiopHolderService {
295294
SupportedVersion.SIOPv2_D12_OID4VP_D20,
296295
])
297296
.withCreateJwtCallback(getCreateJwtCallback(agentContext))
298-
.withVerifyJwtCallback(
299-
getVerifyJwtCallback(agentContext, {
300-
federation: options.federation,
301-
})
302-
)
297+
.withVerifyJwtCallback(getVerifyJwtCallback(agentContext))
303298
.withHasher(Hasher.hash)
304299

305300
const openidProvider = builder.build()

packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderServiceOptions.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,6 @@ export interface OpenId4VcSiopAcceptAuthorizationRequestOptions {
5252
// TODO: Not sure if this also needs the federation because the validation of the authorization is already done with the ResolveAuthorizationRequest
5353
}
5454

55-
export interface OpenId4VcSiopResolveAuthorizationRequestOptions {
56-
federation?: {
57-
/**
58-
* The entity IDs of the trusted issuers.
59-
*/
60-
trustedEntityIds?: string[]
61-
}
62-
}
63-
64-
export interface OpenId4VcSiopGetOpenIdProviderOptions {
65-
federation?: {
66-
/**
67-
* The entity IDs of the trusted issuers.
68-
*/
69-
trustedEntityIds?: string[]
70-
}
71-
}
72-
7355
export interface OpenId4VcSiopResolveTrustChainsOptions {
7456
entityId: string
7557
trustAnchorEntityIds: [string, ...string[]]

packages/openid4vc/src/shared/utils.ts

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
getJwkFromKey,
1717
getKeyFromVerificationMethod,
1818
} from '@credo-ts/core'
19-
import { fetchEntityConfiguration, resolveTrustChains } from '@openid-federation/core'
19+
import { fetchEntityConfiguration } from '@openid-federation/core'
2020

2121
/**
2222
* Returns the JWA Signature Algorithms that are supported by the wallet.
@@ -53,16 +53,7 @@ export async function getKeyFromDid(
5353
return getKeyFromVerificationMethod(verificationMethod)
5454
}
5555

56-
type VerifyJwtCallbackOptions = {
57-
federation?: {
58-
trustedEntityIds?: string[]
59-
}
60-
}
61-
62-
export function getVerifyJwtCallback(
63-
agentContext: AgentContext,
64-
options: VerifyJwtCallbackOptions = {}
65-
): VerifyJwtCallback {
56+
export function getVerifyJwtCallback(agentContext: AgentContext): VerifyJwtCallback {
6657
const logger = agentContext.config.logger
6758

6859
return async (jwtVerifier, jwt) => {
@@ -83,15 +74,9 @@ export function getVerifyJwtCallback(
8374

8475
if (jwtVerifier.method === 'openid-federation') {
8576
const { entityId } = jwtVerifier
86-
const trustedEntityIds = options.federation?.trustedEntityIds
87-
if (!trustedEntityIds) {
88-
logger.error('No trusted entity ids provided but is required for the "openid-federation" method.')
89-
return false
90-
}
9177

92-
const validTrustChains = await resolveTrustChains({
78+
const entityConfiguration = await fetchEntityConfiguration({
9379
entityId,
94-
trustAnchorEntityIds: trustedEntityIds,
9580
verifyJwtCallback: async ({ jwt, jwk }) => {
9681
const res = await jwsService.verifyJws(agentContext, {
9782
jws: jwt,
@@ -101,30 +86,27 @@ export function getVerifyJwtCallback(
10186
return res.isValid
10287
},
10388
})
104-
// When the chain is already invalid we can return false immediately
105-
if (validTrustChains.length === 0) {
106-
logger.error(`${entityId} is not part of a trusted federation.`)
107-
return false
108-
}
10989

110-
// Pick the first valid trust chain for validation of the leaf entity jwks
111-
const { leafEntityConfiguration } = validTrustChains[0]
112-
// TODO: No support yet for signed jwks and external jwks
113-
const rpSigningKeys = leafEntityConfiguration?.metadata?.openid_relying_party?.jwks?.keys
90+
// TODO: Not really sure if we can use the kid of the jwt header for finding the federation key. And if it even has a kid in the jwt header.
91+
const kid = jwt.header.kid
92+
if (!kid) throw new CredoError('No kid found in the jwt header.')
93+
94+
const rpSigningKeys = entityConfiguration.metadata?.openid_relying_party?.jwks?.keys
11495
if (!rpSigningKeys || rpSigningKeys.length === 0)
11596
throw new CredoError('No rp signing keys found in the entity configuration.')
11697

117-
const res = await jwsService.verifyJws(agentContext, {
98+
const jwk = rpSigningKeys.find((key) => key.kid === kid)
99+
if (!jwk) throw new CredoError(`No rp signing key found in the entity configuration with kid: ${kid}.`)
100+
101+
const result = await jwsService.verifyJws(agentContext, {
118102
jws: jwt.raw,
119-
jwkResolver: () => getJwkFromJson(rpSigningKeys[0]),
103+
jwkResolver: () => getJwkFromJson(jwk),
120104
})
121-
if (!res.isValid) {
105+
if (!result.isValid) {
122106
logger.error(`${entityId} does not match the expected signing key.`)
123107
}
124108

125-
// TODO: There is no check yet for the policies
126-
127-
return res.isValid
109+
return result.isValid
128110
}
129111

130112
throw new Error(`Unsupported jwt verifier method: '${jwtVerifier.method}'`)

0 commit comments

Comments
 (0)