diff --git a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml index 9c431f9a..399b9b8b 100644 --- a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml +++ b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml @@ -8,7 +8,7 @@ info: email: info@sphereon.com license: name: Apache 2.0 - url: http://www.apache.org/licenses/LICENSE-2.0.html + url: https://www.apache.org/licenses/LICENSE-2.0.html version: 1.0.0-d38 tags: @@ -2048,16 +2048,16 @@ components: - $ref: '#/components/schemas/CommonMetadata' - $ref: '#/components/schemas/OpenIDConnectDynamicClientRegistrationMetadata' - $ref: '#/components/schemas/IANAOAuthDynamicClientRegistrationMetadata' - type: object - x-tags: - - federation - properties: - client_registration_types: - type: array - items: - $ref: '#/components/schemas/OpenIDConnectRelyingPartyClientRegistrationTypes' - required: - - client_registration_types + - type: object + x-tags: + - federation + properties: + client_registration_types: + type: array + items: + $ref: '#/components/schemas/OpenIDConnectRelyingPartyClientRegistrationTypes' + required: + - client_registration_types OpenIDProviderRequestAuthenticationMethodsSupported: type: object @@ -2081,172 +2081,172 @@ components: - $ref: '#/components/schemas/CommonMetadata' - $ref: '#/components/schemas/OpenIDConnectDiscoveryProviderMetadata' - $ref: '#/components/schemas/IANAOAuthAuthorizationServerMetadata' - type: object - x-tags: - - federation - required: - - client_registration_types_supported - properties: - client_registration_types_supported: - type: array - items: - type: string - description: Array specifying the federation types supported. Values are automatic and explicit. - federation_registration_endpoint: - type: string - format: uri - description: URL of the OP's federation-specific Dynamic Client Registration Endpoint. Must use the https scheme. - request_authentication_methods_supported: - $ref: '#/components/schemas/OpenIDProviderRequestAuthenticationMethodsSupported' - request_authentication_signing_alg_values_supported: - type: array - items: - type: string - description: JSON array containing supported JWS algorithms for signing the JWT used in the request parameter or private_key_jwt of a pushed authorization request. Must include if specified in request_authentication_methods_supported. + - type: object + x-tags: + - federation + required: + - client_registration_types_supported + properties: + client_registration_types_supported: + type: array + items: + type: string + description: Array specifying the federation types supported. Values are automatic and explicit. + federation_registration_endpoint: + type: string + format: uri + description: URL of the OP's federation-specific Dynamic Client Registration Endpoint. Must use the https scheme. + request_authentication_methods_supported: + $ref: '#/components/schemas/OpenIDProviderRequestAuthenticationMethodsSupported' + request_authentication_signing_alg_values_supported: + type: array + items: + type: string + description: JSON array containing supported JWS algorithms for signing the JWT used in the request parameter or private_key_jwt of a pushed authorization request. Must include if specified in request_authentication_methods_supported. OAuthAuthorizationServerMetadata: allOf: - $ref: '#/components/schemas/CommonMetadata' - $ref: '#/components/schemas/IANAOAuthAuthorizationServerMetadata' - type: object - x-tags: - - federation - properties: - issuer: - type: string - description: > - The authorization server's issuer identifier, which is - a URL that uses the "https" scheme and has no query or fragment - components. Authorization server metadata is published at a - location that is ".well-known" according to RFC 5785 derived from - this issuer identifier. The issuer identifier is used to prevent - authorization server mix-up attacks. - example: "https://example.com" - authorization_endpoint: - type: string - description: URL of the authorization server's authorization endpoint. - example: "https://example.com/oauth2/authorize" - token_endpoint: - type: string - description: URL of the authorization server's token endpoint. - example: "https://example.com/oauth2/token" - jwks_uri: - type: string - description: URL of the authorization server's JWK Set document. - example: "https://example.com/oauth2/jwks" - registration_endpoint: - type: string - description: URL of the authorization server's OAuth 2.0 Dynamic Client Registration endpoint. - example: "https://example.com/oauth2/register" - scopes_supported: - type: array - items: - type: string - description: JSON array containing a list of the OAuth 2.0 "scope" values that this authorization server supports. - example: [ "openid", "profile", "email" ] - response_types_supported: - type: array - items: - type: string - description: JSON array containing a list of the OAuth 2.0 "response_type" values that this authorization server supports. - example: [ "code", "token", "id_token" ] - response_modes_supported: - type: array - items: - type: string - description: JSON array containing a list of the OAuth 2.0 "response_mode" values that this authorization server supports. - example: [ "query", "fragment", "form_post" ] - grant_types_supported: - type: array - items: - type: string - description: JSON array containing a list of the OAuth 2.0 grant type values that this authorization server supports. - example: [ "authorization_code", "implicit", "client_credentials", "refresh_token" ] - token_endpoint_auth_methods_supported: - type: array - items: - type: string - description: JSON array containing a list of client authentication methods supported by this token endpoint. - example: [ "client_secret_basic", "private_key_jwt" ] - token_endpoint_auth_signing_alg_values_supported: - type: array - items: - type: string - description: JSON array containing a list of the JWS signing algorithms supported by the token endpoint for the signature on the JWT used to authenticate the client. - example: [ "RS256", "ES256" ] - service_documentation: - type: string - description: URL of a page containing human-readable information that developers might want or need to know when using the authorization server. - example: "https://example.com/service_documentation" - ui_locales_supported: - type: array - items: - type: string - description: Languages and scripts supported for the user interface, represented as a JSON array of language tag values from BCP 47. - example: [ "en-US", "fr-FR" ] - op_policy_uri: - type: string - description: URL that the authorization server provides to the person registering the client to read about the authorization server's requirements on how the client can use the data provided by the authorization server. - example: "https://example.com/op_policy" - op_tos_uri: - type: string - description: URL that the authorization server provides to the person registering the client to read about the authorization server's terms of service. - example: "https://example.com/op_tos" - revocation_endpoint: - type: string - description: URL of the authorization server's OAuth 2.0 revocation endpoint. - example: "https://example.com/oauth2/revoke" - revocation_endpoint_auth_methods_supported: - type: array - items: - type: string - description: JSON array containing a list of client authentication methods supported by this revocation endpoint. - example: [ "client_secret_basic", "private_key_jwt" ] - revocation_endpoint_auth_signing_alg_values_supported: - type: array - items: - type: string - description: JSON array containing a list of the JWS signing algorithms supported by the revocation endpoint for the signature on the JWT used to authenticate the client. - example: [ "RS256", "ES256" ] - introspection_endpoint: - type: string - description: URL of the authorization server's OAuth 2.0 introspection endpoint. - example: "https://example.com/oauth2/introspect" - introspection_endpoint_auth_methods_supported: - type: array - items: - type: string - description: JSON array containing a list of client authentication methods supported by this introspection endpoint. - example: [ "client_secret_basic", "private_key_jwt" ] - introspection_endpoint_auth_signing_alg_values_supported: - type: array - items: - type: string - description: JSON array containing a list of the JWS signing algorithms supported by the introspection endpoint for the signature on the JWT used to authenticate the client. - example: [ "RS256", "ES256" ] - code_challenge_methods_supported: - type: array - items: - type: string - description: JSON array containing a list of Proof Key for Code Exchange (PKCE) code challenge methods supported by this authorization server. - example: [ "plain", "S256" ] + - type: object + x-tags: + - federation + properties: + issuer: + type: string + description: > + The authorization server's issuer identifier, which is + a URL that uses the "https" scheme and has no query or fragment + components. Authorization server metadata is published at a + location that is ".well-known" according to RFC 5785 derived from + this issuer identifier. The issuer identifier is used to prevent + authorization server mix-up attacks. + example: "https://example.com" + authorization_endpoint: + type: string + description: URL of the authorization server's authorization endpoint. + example: "https://example.com/oauth2/authorize" + token_endpoint: + type: string + description: URL of the authorization server's token endpoint. + example: "https://example.com/oauth2/token" + jwks_uri: + type: string + description: URL of the authorization server's JWK Set document. + example: "https://example.com/oauth2/jwks" + registration_endpoint: + type: string + description: URL of the authorization server's OAuth 2.0 Dynamic Client Registration endpoint. + example: "https://example.com/oauth2/register" + scopes_supported: + type: array + items: + type: string + description: JSON array containing a list of the OAuth 2.0 "scope" values that this authorization server supports. + example: [ "openid", "profile", "email" ] + response_types_supported: + type: array + items: + type: string + description: JSON array containing a list of the OAuth 2.0 "response_type" values that this authorization server supports. + example: [ "code", "token", "id_token" ] + response_modes_supported: + type: array + items: + type: string + description: JSON array containing a list of the OAuth 2.0 "response_mode" values that this authorization server supports. + example: [ "query", "fragment", "form_post" ] + grant_types_supported: + type: array + items: + type: string + description: JSON array containing a list of the OAuth 2.0 grant type values that this authorization server supports. + example: [ "authorization_code", "implicit", "client_credentials", "refresh_token" ] + token_endpoint_auth_methods_supported: + type: array + items: + type: string + description: JSON array containing a list of client authentication methods supported by this token endpoint. + example: [ "client_secret_basic", "private_key_jwt" ] + token_endpoint_auth_signing_alg_values_supported: + type: array + items: + type: string + description: JSON array containing a list of the JWS signing algorithms supported by the token endpoint for the signature on the JWT used to authenticate the client. + example: [ "RS256", "ES256" ] + service_documentation: + type: string + description: URL of a page containing human-readable information that developers might want or need to know when using the authorization server. + example: "https://example.com/service_documentation" + ui_locales_supported: + type: array + items: + type: string + description: Languages and scripts supported for the user interface, represented as a JSON array of language tag values from BCP 47. + example: [ "en-US", "fr-FR" ] + op_policy_uri: + type: string + description: URL that the authorization server provides to the person registering the client to read about the authorization server's requirements on how the client can use the data provided by the authorization server. + example: "https://example.com/op_policy" + op_tos_uri: + type: string + description: URL that the authorization server provides to the person registering the client to read about the authorization server's terms of service. + example: "https://example.com/op_tos" + revocation_endpoint: + type: string + description: URL of the authorization server's OAuth 2.0 revocation endpoint. + example: "https://example.com/oauth2/revoke" + revocation_endpoint_auth_methods_supported: + type: array + items: + type: string + description: JSON array containing a list of client authentication methods supported by this revocation endpoint. + example: [ "client_secret_basic", "private_key_jwt" ] + revocation_endpoint_auth_signing_alg_values_supported: + type: array + items: + type: string + description: JSON array containing a list of the JWS signing algorithms supported by the revocation endpoint for the signature on the JWT used to authenticate the client. + example: [ "RS256", "ES256" ] + introspection_endpoint: + type: string + description: URL of the authorization server's OAuth 2.0 introspection endpoint. + example: "https://example.com/oauth2/introspect" + introspection_endpoint_auth_methods_supported: + type: array + items: + type: string + description: JSON array containing a list of client authentication methods supported by this introspection endpoint. + example: [ "client_secret_basic", "private_key_jwt" ] + introspection_endpoint_auth_signing_alg_values_supported: + type: array + items: + type: string + description: JSON array containing a list of the JWS signing algorithms supported by the introspection endpoint for the signature on the JWT used to authenticate the client. + example: [ "RS256", "ES256" ] + code_challenge_methods_supported: + type: array + items: + type: string + description: JSON array containing a list of Proof Key for Code Exchange (PKCE) code challenge methods supported by this authorization server. + example: [ "plain", "S256" ] OAuthClientMetadata: allOf: - $ref: '#/components/schemas/CommonMetadata' - $ref: '#/components/schemas/OAuthDynamicClientMetadata' - $ref: '#/components/schemas/IANAOAuthDynamicClientRegistrationMetadata' - type: object - x-tags: - - federation + - type: object + x-tags: + - federation OAuthProtectedResourceMetadata: allOf: - $ref: '#/components/schemas/CommonMetadata' - $ref: '#/components/schemas/ProtectedResourceMetadata' - type: object - x-tags: - - federation + - type: object + x-tags: + - federation ProtectedResourceMetadata: type: object diff --git a/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.kt b/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.kt index 579c68d1..5c294565 100644 --- a/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.kt +++ b/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.kt @@ -9,6 +9,7 @@ import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive + interface ICryptoService { suspend fun verify( jwt: String, diff --git a/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/trustchain/TrustChain.kt b/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/trustchain/TrustChain.kt index 80e35033..687a97dd 100644 --- a/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/trustchain/TrustChain.kt +++ b/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/trustchain/TrustChain.kt @@ -27,17 +27,24 @@ class SimpleCache { } } -class TrustChain(private val fetchService: IFetchCallbackService, private val cryptoService: ICryptoCallbackService) { +class TrustChain( + private val fetchService: IFetchCallbackService, + private val cryptoService: ICryptoCallbackService +) { suspend fun resolve( entityIdentifier: String, trustAnchors: Array ): MutableList? { val cache = SimpleCache() val chain: MutableList = arrayListOf() + return try { - ClientConstants.LOG.debug("Resolving trust chain for entity $entityIdentifier and trust anchors $trustAnchors") + ClientConstants.LOG.debug("Resolving trust chain for entity: $entityIdentifier with trust anchors: ${trustAnchors.joinToString()}") buildTrustChainRecursive(entityIdentifier, trustAnchors, chain, cache) } catch (e: Exception) { - ClientConstants.LOG.debug("Failed to resolve trust chain for entity $entityIdentifier and trust anchors $trustAnchors, error: $e") + ClientConstants.LOG.error( + "Failed to resolve trust chain for entity: $entityIdentifier with trust anchors: ${trustAnchors.joinToString()}", + e + ) null } } @@ -48,45 +55,77 @@ class TrustChain(private val fetchService: IFetchCallbackService, private val cr chain: MutableList, cache: SimpleCache ): MutableList? { - val entityConfigurationJwt = this.fetchService.fetchStatement(getEntityConfigurationEndpoint(entityIdentifier)) - val decodedEntityConfiguration = decodeJWTComponents(entityConfigurationJwt) + ClientConstants.LOG.debug("Fetching entity configuration for $entityIdentifier") - val key = findKeyInJwks( - decodedEntityConfiguration.payload["jwks"]?.jsonObject?.get("keys")?.jsonArray ?: return null, - decodedEntityConfiguration.header.kid - ) + try { + val entityConfigurationJwt = + this.fetchService.fetchStatement(getEntityConfigurationEndpoint(entityIdentifier)) + if (entityConfigurationJwt == null) { + ClientConstants.LOG.debug("Failed to fetch entity configuration JWT for $entityIdentifier") + return null + } - if (key == null) return null + val decodedEntityConfiguration = decodeJWTComponents(entityConfigurationJwt) + if (decodedEntityConfiguration == null) { + ClientConstants.LOG.debug("Failed to decode entity configuration JWT for $entityIdentifier") + return null + } + val keysArray = decodedEntityConfiguration.payload["jwks"]?.jsonObject?.get("keys")?.jsonArray + if (keysArray == null) { + ClientConstants.LOG.debug("No keys found in JWKS for $entityIdentifier") + return null + } - if (!cryptoService.verify(entityConfigurationJwt, key)) { - return null - } + val key = findKeyInJwks(keysArray, decodedEntityConfiguration.header.kid) + if (key == null) { + ClientConstants.LOG.debug("Key with kid ${decodedEntityConfiguration.header.kid} not found for $entityIdentifier") + return null + } - val entityStatement: EntityConfigurationStatement = - mapEntityStatement(entityConfigurationJwt, EntityConfigurationStatement::class) ?: return null + ClientConstants.LOG.debug("Verifying entity configuration JWT for $entityIdentifier") + if (!cryptoService.verify(entityConfigurationJwt, key)) { + ClientConstants.LOG.debug("Verification failed for entity configuration JWT of $entityIdentifier") + return null + } - if (chain.isEmpty()) { - chain.add(entityConfigurationJwt) - } + val entityStatement = mapEntityStatement(entityConfigurationJwt, EntityConfigurationStatement::class) + if (entityStatement == null) { + ClientConstants.LOG.debug("Failed to map entity configuration statement for $entityIdentifier") + return null + } + + if (chain.isEmpty()) { + chain.add(entityConfigurationJwt) + } - val authorityHints = entityStatement.authorityHints ?: return null + val authorityHints = entityStatement.authorityHints + if (authorityHints == null) { + ClientConstants.LOG.debug("No authority hints found for $entityIdentifier") + return null + } - for (authority in authorityHints) { - val result = processAuthority( - authority, - entityIdentifier, - trustAnchors, - chain, - decodedEntityConfiguration.header.kid, - cache - ) + ClientConstants.LOG.debug("Processing authority hints for $entityIdentifier") + for (authority in authorityHints) { + val result = processAuthority( + authority, + entityIdentifier, + trustAnchors, + chain, + decodedEntityConfiguration.header.kid, + cache + ) - if (result != null) { - return result + if (result != null) { + return result + } } - } - return null + ClientConstants.LOG.debug("No matching trust chain found for $entityIdentifier") + return null + } catch (e: Exception) { + ClientConstants.LOG.debug("Failed to fetch entity configuration for $entityIdentifier, error: $e") + return null + } } private suspend fun processAuthority( @@ -97,85 +136,117 @@ class TrustChain(private val fetchService: IFetchCallbackService, private val cr lastStatementKid: String, cache: SimpleCache ): MutableList? { + ClientConstants.LOG.debug("Processing authority $authority") try { val authorityConfigurationEndpoint = getEntityConfigurationEndpoint(authority) + ClientConstants.LOG.debug("Authority configuration endpoint: $authorityConfigurationEndpoint") // Avoid processing the same entity twice - if (cache.get(authorityConfigurationEndpoint) != null) return null + if (cache.get(authorityConfigurationEndpoint) != null) { + ClientConstants.LOG.debug("Authority $authority has already been processed; skipping") + return null + } val authorityEntityConfigurationJwt = fetchService.fetchStatement(authorityConfigurationEndpoint) + ClientConstants.LOG.debug("Fetched authority entity configuration JWT for $authority") + cache.put(authorityConfigurationEndpoint, authorityEntityConfigurationJwt) val decodedJwt = decodeJWTComponents(authorityEntityConfigurationJwt) val kid = decodedJwt.header.kid + ClientConstants.LOG.debug("Decoded JWT for $authority with kid: $kid") val key = findKeyInJwks( decodedJwt.payload["jwks"]?.jsonObject?.get("keys")?.jsonArray ?: return null, kid ) - if (key == null) return null + if (key == null) { + ClientConstants.LOG.debug("No valid key found for kid $kid in $authority") + return null + } - if (!cryptoService.verify( - authorityEntityConfigurationJwt, - key - ) - ) { + ClientConstants.LOG.debug("Verifying JWT for $authority") + if (!cryptoService.verify(authorityEntityConfigurationJwt, key)) { + ClientConstants.LOG.debug("Verification failed for JWT of $authority") return null } - val authorityEntityConfiguration: EntityConfigurationStatement = - mapEntityStatement(authorityEntityConfigurationJwt, EntityConfigurationStatement::class) ?: return null + val authorityEntityConfiguration = + mapEntityStatement(authorityEntityConfigurationJwt, EntityConfigurationStatement::class) + if (authorityEntityConfiguration == null) { + ClientConstants.LOG.debug("Failed to map entity configuration statement for $authority") + return null + } val federationEntityMetadata = authorityEntityConfiguration.metadata?.get("federation_entity") as? JsonObject - if (federationEntityMetadata == null || !federationEntityMetadata.containsKey("federation_fetch_endpoint")) return null + if (federationEntityMetadata == null || !federationEntityMetadata.containsKey("federation_fetch_endpoint")) { + ClientConstants.LOG.debug("No federation fetch endpoint found for $authority") + return null + } val authorityEntityFetchEndpoint = - federationEntityMetadata["federation_fetch_endpoint"]?.jsonPrimitive?.content ?: return null + federationEntityMetadata["federation_fetch_endpoint"]?.jsonPrimitive?.content + ?: return null + ClientConstants.LOG.debug("Authority entity fetch endpoint: $authorityEntityFetchEndpoint") val subordinateStatementEndpoint = getSubordinateStatementEndpoint(authorityEntityFetchEndpoint, entityIdentifier) + ClientConstants.LOG.debug("Subordinate statement endpoint: $subordinateStatementEndpoint") val subordinateStatementJwt = fetchService.fetchStatement(subordinateStatementEndpoint) + ClientConstants.LOG.debug("Fetched subordinate statement JWT for $entityIdentifier") val decodedSubordinateStatement = decodeJWTComponents(subordinateStatementJwt) - val subordinateStatementKey = findKeyInJwks( - decodedJwt.payload["jwks"]?.jsonObject?.get("keys")?.jsonArray - ?: return null, + decodedJwt.payload["jwks"]?.jsonObject?.get("keys")?.jsonArray ?: return null, decodedSubordinateStatement.header.kid ) - if (subordinateStatementKey == null) return null + if (subordinateStatementKey == null) { + ClientConstants.LOG.debug("No valid key found for subordinate statement of $entityIdentifier") + return null + } + ClientConstants.LOG.debug("Verifying subordinate statement JWT for $entityIdentifier") if (!cryptoService.verify(subordinateStatementJwt, subordinateStatementKey)) { + ClientConstants.LOG.debug("Verification failed for subordinate statement JWT of $entityIdentifier") return null } - val subordinateStatement: SubordinateStatement = - mapEntityStatement(subordinateStatementJwt, SubordinateStatement::class) ?: return null + val subordinateStatement = mapEntityStatement(subordinateStatementJwt, SubordinateStatement::class) + if (subordinateStatement == null) { + ClientConstants.LOG.debug("Failed to map subordinate statement for $entityIdentifier") + return null + } val jwks = subordinateStatement.jwks val keys = jwks.propertyKeys ?: return null - // Check if the entity key exists in subordinate statement + // Check if the entity key exists in the subordinate statement val entityKeyExistsInSubordinateStatement = checkKidInJwks(keys, lastStatementKid) - if (!entityKeyExistsInSubordinateStatement) return null + if (!entityKeyExistsInSubordinateStatement) { + ClientConstants.LOG.debug("Entity key does not exist in the subordinate statement for $entityIdentifier") + return null + } + // If authority is in trust anchors, return the completed chain if (trustAnchors.contains(authority)) { chain.add(subordinateStatementJwt) chain.add(authorityEntityConfigurationJwt) + ClientConstants.LOG.debug("Trust anchor $authority found; returning completed chain") return chain } // Recursively build trust chain if there are authority hints if (authorityEntityConfiguration.authorityHints?.isNotEmpty() == true) { + ClientConstants.LOG.debug("Processing authority hints for $authority") chain.add(subordinateStatementJwt) - val result = - buildTrustChainRecursive(authority, trustAnchors, chain, cache) + val result = buildTrustChainRecursive(authority, trustAnchors, chain, cache) if (result != null) return result + ClientConstants.LOG.debug("Removing last element from the chain for $authority") chain.removeLast() } } catch (e: Exception) { diff --git a/modules/openid-federation-client/src/jsMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.js.kt b/modules/openid-federation-client/src/jsMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.js.kt index 1f017988..04f76862 100644 --- a/modules/openid-federation-client/src/jsMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.js.kt +++ b/modules/openid-federation-client/src/jsMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.js.kt @@ -1,5 +1,6 @@ package com.sphereon.oid.fed.client.crypto +import com.sphereon.oid.fed.client.ClientConstants import com.sphereon.oid.fed.client.mapper.decodeJWTComponents import com.sphereon.oid.fed.client.types.ICallbackService import com.sphereon.oid.fed.openapi.models.Jwk @@ -80,15 +81,22 @@ actual fun cryptoService(): ICryptoCallbackService = CryptoServiceJSAdapterObjec actual suspend fun verifyImpl(jwt: String, key: Jwk): Boolean { try { + ClientConstants.LOG.debug("Verifying JWT with key: ${Json.encodeToString(key)}") + val decodedJwt = decodeJWTComponents(jwt) + ClientConstants.LOG.debug("Decoded JWT header: ${Json.encodeToString(decodedJwt.header)}") + val publicKey = Jose.importJWK( - JSON.parse(Json.encodeToString(key)), alg = decodedJwt.header.alg ?: "RS256" + JSON.parse(Json.encodeToString(key)), + alg = decodedJwt.header.alg ?: "RS256" ).await() val verification = Jose.jwtVerify(jwt, publicKey).await() + return verification != undefined } catch (e: Throwable) { + ClientConstants.LOG.error("Error during JWT verification", e) return false } }