Skip to content

Commit

Permalink
feat: Added more logging and added unhappy tests
Browse files Browse the repository at this point in the history
Signed-off-by: Tom Lanser <[email protected]>
  • Loading branch information
Tommylans committed Nov 18, 2024
1 parent dcd810d commit cb6d70f
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,4 @@ export class OpenId4VcIssuerModuleConfig {
endpointPath: userOptions.endpointPath ?? '/offers',
}
}

public get federationEndpoint(): OpenId4VcSiopFederationEndpointConfig | undefined {
const userOptions = this.options.endpoints.federation
if (!userOptions) return undefined

return {
...userOptions,
endpointPath: userOptions.endpointPath ?? '/.well-known/openid-federation',
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,6 @@ export function configureFederationEndpoint(router: Router) {
})

const issuerMetadata = openId4VcIssuerService.getIssuerMetadata(agentContext, issuer)
// TODO: Use a type here from sphreon
const transformedMetadata = {
credential_issuer: issuerMetadata.issuerUrl,
token_endpoint: issuerMetadata.tokenEndpoint,
credential_endpoint: issuerMetadata.credentialEndpoint,
authorization_server: issuerMetadata.authorizationServer,
authorization_servers: issuerMetadata.authorizationServer ? [issuerMetadata.authorizationServer] : undefined,
credentials_supported: issuerMetadata.credentialsSupported,
credential_configurations_supported: issuerMetadata.credentialConfigurationsSupported,
display: issuerMetadata.issuerDisplay,
dpop_signing_alg_values_supported: issuerMetadata.dpopSigningAlgValuesSupported,
} as const

const now = new Date()
const expires = new Date(now.getTime() + 1000 * 60 * 60 * 24) // 1 day from now
Expand Down Expand Up @@ -68,7 +56,19 @@ export function configureFederationEndpoint(router: Router) {
}
: undefined,
openid_provider: {
...transformedMetadata,
// TODO: The type isn't correct yet down the line so that needs to be updated before
// credential_issuer: issuerMetadata.issuerUrl,
// token_endpoint: issuerMetadata.tokenEndpoint,
// credential_endpoint: issuerMetadata.credentialEndpoint,
// authorization_server: issuerMetadata.authorizationServer,
// authorization_servers: issuerMetadata.authorizationServer
// ? [issuerMetadata.authorizationServer]
// : undefined,
// credentials_supported: issuerMetadata.credentialsSupported,
// credential_configurations_supported: issuerMetadata.credentialConfigurationsSupported,
// display: issuerMetadata.issuerDisplay,
// dpop_signing_alg_values_supported: issuerMetadata.dpopSigningAlgValuesSupported,

client_registration_types_supported: ['automatic'],
jwks: {
keys: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,44 +113,5 @@ describe('OpenId4VcVerifier', () => {
expect(jwt.payload.iss).toEqual(verifier.did)
expect(jwt.payload.sub).toEqual(verifier.did)
})

it('check openid proof request format (entity id)', async () => {
const openIdVerifier = await verifier.agent.modules.openId4VcVerifier.createVerifier()
const { authorizationRequest, verificationSession } =
await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({
requestSigner: {
method: 'openid-federation',
clientId: 'http://localhost:3001/verifier',
},
verifierId: openIdVerifier.verifierId,
})

expect(
authorizationRequest.startsWith(
`openid://?client_id=${encodeURIComponent(verifier.did)}&request_uri=http%3A%2F%2Fredirect-uri%2F${
openIdVerifier.verifierId
}%2Fauthorization-requests%2F`
)
).toBe(true)

const jwt = Jwt.fromSerializedJwt(verificationSession.authorizationRequestJwt)

expect(jwt.header.kid)

expect(jwt.header.kid).toEqual(verifier.kid)
expect(jwt.header.alg).toEqual(SigningAlgo.EDDSA)
expect(jwt.header.typ).toEqual('JWT')
expect(jwt.payload.additionalClaims.scope).toEqual('openid')
expect(jwt.payload.additionalClaims.client_id).toEqual(verifier.did)
expect(jwt.payload.additionalClaims.response_uri).toEqual(
`http://redirect-uri/${openIdVerifier.verifierId}/authorize`
)
expect(jwt.payload.additionalClaims.response_mode).toEqual('direct_post')
expect(jwt.payload.additionalClaims.nonce).toBeDefined()
expect(jwt.payload.additionalClaims.state).toBeDefined()
expect(jwt.payload.additionalClaims.response_type).toEqual('id_token')
expect(jwt.payload.iss).toEqual(verifier.did)
expect(jwt.payload.sub).toEqual(verifier.did)
})
})
})
18 changes: 13 additions & 5 deletions packages/openid4vc/src/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export function getVerifyJwtCallback(
agentContext: AgentContext,
options: VerifyJwtCallbackOptions = {}
): VerifyJwtCallback {
const logger = agentContext.config.logger

return async (jwtVerifier, jwt) => {
const jwsService = agentContext.dependencyManager.resolve(JwsService)

Expand All @@ -79,8 +81,10 @@ export function getVerifyJwtCallback(
if (jwtVerifier.method === 'openid-federation') {
const { entityId } = jwtVerifier
const trustedEntityIds = options.federation?.trustedEntityIds
if (!trustedEntityIds)
throw new CredoError('No trusted entity ids provided but is required for the openid-federation method.')
if (!trustedEntityIds) {
logger.error('No trusted entity ids provided but is required for the "openid-federation" method.')
return false
}

const validTrustChains = await resolveTrustChains({
entityId,
Expand All @@ -95,7 +99,10 @@ export function getVerifyJwtCallback(
},
})
// When the chain is already invalid we can return false immediately
if (validTrustChains.length === 0) return false
if (validTrustChains.length === 0) {
logger.error(`${entityId} is not part of a trusted federation.`)
return false
}

// Pick the first valid trust chain for validation of the leaf entity jwks
const { entityConfiguration } = validTrustChains[0]
Expand All @@ -108,11 +115,12 @@ export function getVerifyJwtCallback(
jws: jwt.raw,
jwkResolver: () => getJwkFromJson(rpSigningKeys[0]),
})
if (!res.isValid) {
logger.error(`${entityId} does not match the expected signing key.`)
}

// TODO: There is no check yet for the policies

// TODO: When this function results in a `false` it gives a really misleading error message: 'Error verifying the DID Auth Token signature.'

return res.isValid
}

Expand Down
92 changes: 92 additions & 0 deletions packages/openid4vc/tests/openid4vc-federation.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,4 +424,96 @@ describe('OpenId4Vc', () => {
],
})
})

it('e2e flow with tenants, unhappy flow', async () => {
const holderTenant = await holder.agent.modules.tenants.getTenantAgent({ tenantId: holder1.tenantId })
const verifierTenant1 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier1.tenantId })
const verifierTenant2 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier2.tenantId })

const openIdVerifierTenant1 = await verifierTenant1.modules.openId4VcVerifier.createVerifier()
const openIdVerifierTenant2 = await verifierTenant2.modules.openId4VcVerifier.createVerifier()

const signedCredential1 = await issuer.agent.w3cCredentials.signCredential({
format: ClaimFormat.JwtVc,
credential: new W3cCredential({
type: ['VerifiableCredential', 'OpenBadgeCredential'],
issuer: new W3cIssuer({ id: issuer.did }),
credentialSubject: new W3cCredentialSubject({ id: holder1.did }),
issuanceDate: w3cDate(Date.now()),
}),
alg: JwaSignatureAlgorithm.EdDSA,
verificationMethod: issuer.verificationMethod.id,
})

const signedCredential2 = await issuer.agent.w3cCredentials.signCredential({
format: ClaimFormat.JwtVc,
credential: new W3cCredential({
type: ['VerifiableCredential', 'UniversityDegreeCredential'],
issuer: new W3cIssuer({ id: issuer.did }),
credentialSubject: new W3cCredentialSubject({ id: holder1.did }),
issuanceDate: w3cDate(Date.now()),
}),
alg: JwaSignatureAlgorithm.EdDSA,
verificationMethod: issuer.verificationMethod.id,
})

await holderTenant.w3cCredentials.storeCredential({ credential: signedCredential1 })
await holderTenant.w3cCredentials.storeCredential({ credential: signedCredential2 })

const { authorizationRequest: authorizationRequestUri1, verificationSession: verificationSession1 } =
await verifierTenant1.modules.openId4VcVerifier.createAuthorizationRequest({
verifierId: openIdVerifierTenant1.verifierId,
requestSigner: {
method: 'openid-federation',
},
presentationExchange: {
definition: openBadgePresentationDefinition,
},
})

expect(authorizationRequestUri1).toEqual(
`openid4vp://?client_id=${encodeURIComponent(
`http://localhost:1234/oid4vp/${openIdVerifierTenant1.verifierId}`
)}&request_uri=${encodeURIComponent(verificationSession1.authorizationRequestUri)}`
)

const { authorizationRequest: authorizationRequestUri2, verificationSession: verificationSession2 } =
await verifierTenant2.modules.openId4VcVerifier.createAuthorizationRequest({
requestSigner: {
method: 'openid-federation',
},
presentationExchange: {
definition: universityDegreePresentationDefinition,
},
verifierId: openIdVerifierTenant2.verifierId,
})

expect(authorizationRequestUri2).toEqual(
`openid4vp://?client_id=${encodeURIComponent(
`http://localhost:1234/oid4vp/${openIdVerifierTenant2.verifierId}`
)}&request_uri=${encodeURIComponent(verificationSession2.authorizationRequestUri)}`
)

await verifierTenant1.endSession()
await verifierTenant2.endSession()

const resolvedProofRequestWithFederationPromise =
holderTenant.modules.openId4VcHolder.resolveSiopAuthorizationRequest(authorizationRequestUri1, {
federation: {
// This will look for a whole different trusted entity
trustedEntityIds: [`http://localhost:1234/oid4vp/${openIdVerifierTenant2.verifierId}`],
},
})

// TODO: Look into this error see if we can make it more specific
await expect(resolvedProofRequestWithFederationPromise).rejects.toThrow(
`Error verifying the DID Auth Token signature.`
)

const resolvedProofRequestWithoutFederationPromise =
holderTenant.modules.openId4VcHolder.resolveSiopAuthorizationRequest(authorizationRequestUri2)
await expect(resolvedProofRequestWithoutFederationPromise).rejects.toThrow(
`Error verifying the DID Auth Token signature.`
)
})
})

0 comments on commit cb6d70f

Please sign in to comment.