From 9eb39274c119b293c26dd4e81be674d06fda9fd4 Mon Sep 17 00:00:00 2001 From: Ahmed Moussa Date: Wed, 30 Aug 2023 16:19:47 +0300 Subject: [PATCH] fix(pollux): fix JWTPayload serlization & Update Error Handling (#97) Signed-off-by: Ahmed Moussa --- .../prism/walletsdk/domain/models/Errors.kt | 24 ++++++----- .../walletsdk/domain/models/JWTPayload.kt | 41 ++++++++++++++++++- .../prism/walletsdk/pollux/JWTCredential.kt | 3 +- .../prism/walletsdk/pollux/PolluxImpl.kt | 2 +- .../prism/walletsdk/prismagent/PrismAgent.kt | 2 +- 5 files changed, 55 insertions(+), 17 deletions(-) diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/Errors.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/Errors.kt index 2337c771f..538127bd7 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/Errors.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/Errors.kt @@ -16,7 +16,7 @@ abstract interface Error { * This object may include an error code, an error message, and possibly an array of underlying errors. If the error * received does not conform to the [Error] interface, it will be classified as an [UnknownPrismError]. */ -abstract class UnknownPrismError : Error, Throwable() { +abstract class UnknownPrismError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : Error, Throwable(message, cause) { override val code: Int? get() = null @@ -36,7 +36,7 @@ abstract class UnknownPrismError : Error, Throwable() { * This object may include an error code and an error message. If the error received conforms to the [KnownPrismError], * it will be classified as a known error. */ -abstract class KnownPrismError : Error, Throwable() { +abstract class KnownPrismError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : Error, Throwable(message, cause) { override val code: Int? get() = null override val message: String? @@ -54,12 +54,14 @@ abstract class KnownPrismError : Error, Throwable() { * This object may include an error code, an error message, and possibly an array of underlying errors. * If the error received does not conform to the [KnownPrismError], it will be classified as an [UnknownPrismError]. */ -abstract class UnknownError : UnknownPrismError() { +abstract class UnknownError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : UnknownPrismError(message, cause) { class SomethingWentWrongError( + message: String? = null, + cause: Throwable? = null, private val customMessage: String? = null, private val customUnderlyingErrors: Array = emptyArray() - ) : UnknownError() { + ) : UnknownError(message, cause) { override val code: Int? = -1 override val message: String? get() { @@ -76,7 +78,7 @@ abstract class UnknownError : UnknownPrismError() { * This object may include an error code and an error message. If the error received conforms to the [KnownPrismError], * it will be classified as a known error. */ -sealed class CommonError : KnownPrismError() { +sealed class CommonError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : KnownPrismError(message, cause) { class InvalidURLError(val url: String) : CommonError() { override val code: Int get() = -2 @@ -101,7 +103,7 @@ sealed class CommonError : KnownPrismError() { * This object may include an error code and an error message. If the error received conforms to the [KnownPrismError], * it will be classified as a known error. */ -sealed class ApolloError : KnownPrismError() { +sealed class ApolloError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : KnownPrismError(message, cause) { class InvalidMnemonicWord(private val invalidWords: Array? = null) : ApolloError() { override val code: Int get() = 11 @@ -147,7 +149,7 @@ sealed class ApolloError : KnownPrismError() { * This object may include an error code and an error message. If the error received conforms to the [KnownPrismError], * it will be classified as a known error. */ -sealed class CastorError : KnownPrismError() { +sealed class CastorError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : KnownPrismError(message, cause) { class KeyCurveNotSupported(val curve: String) : CastorError() { override val code: Int get() = 21 @@ -234,7 +236,7 @@ sealed class CastorError : KnownPrismError() { * This object may include an error code and an error message. If the error received conforms to the [KnownPrismError], * it will be classified as a known error. */ -sealed class MercuryError : KnownPrismError() { +sealed class MercuryError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : KnownPrismError(message, cause) { class NoDIDReceiverSetError : MercuryError() { override val code: Int @@ -307,7 +309,7 @@ sealed class MercuryError : KnownPrismError() { * This object may include an error code and an error message. If the error received conforms to the [KnownPrismError], * it will be classified as a known error. */ -sealed class PlutoError : KnownPrismError() { +sealed class PlutoError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : KnownPrismError(message, cause) { class MissingDataPersistence(val type: String, private val affecting: String) : PlutoError() { override val code: Int @@ -383,7 +385,7 @@ sealed class PlutoError : KnownPrismError() { * This object may include an error code and an error message. If the error received conforms to the [KnownPrismError], * it will be classified as a known error. */ -sealed class PolluxError : KnownPrismError() { +sealed class PolluxError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : KnownPrismError(message, cause) { class InvalidPrismDID : PolluxError() { override val code: Int get() = 53 @@ -392,7 +394,7 @@ sealed class PolluxError : KnownPrismError() { get() = "To create a JWT presentation a Prism DID is required" } - class InvalidCredentialError : PolluxError() { + class InvalidCredentialError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : PolluxError(message, cause) { override val code: Int get() = 51 diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/JWTPayload.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/JWTPayload.kt index 2c57d3cfe..b751da5e6 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/JWTPayload.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/JWTPayload.kt @@ -2,6 +2,7 @@ package io.iohk.atala.prism.walletsdk.domain.models import io.iohk.atala.prism.walletsdk.domain.VC import kotlinx.serialization.EncodeDefault +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonArray @@ -15,14 +16,17 @@ import kotlinx.serialization.json.jsonPrimitive * *Note: This data class conforms to the JSON Web Token (JWT) format. For more information, see https://jwt.io/introduction/. */ +@OptIn(ExperimentalSerializationApi::class) @Serializable -data class JWTPayload @JvmOverloads constructor( +data class JWTPayload +@JvmOverloads constructor( val iss: String, val sub: String?, @SerialName(VC) val verifiableCredential: JWTVerifiableCredential, val nbf: Long?, - val exp: Long?, + @EncodeDefault + val exp: Long? = null, @EncodeDefault val jti: String? = null, @EncodeDefault @@ -75,6 +79,39 @@ data class JWTPayload @JvmOverloads constructor( return result } } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as JWTPayload + + if (iss != other.iss) return false + if (sub != other.sub) return false + if (verifiableCredential != other.verifiableCredential) return false + if (nbf != other.nbf) return false + if (exp != other.exp) return false + if (jti != other.jti) return false + if (aud != null) { + if (other.aud == null) return false + if (!aud.contentEquals(other.aud)) return false + } else if (other.aud != null) return false + if (originalJWTString != other.originalJWTString) return false + + return true + } + + override fun hashCode(): Int { + var result = iss.hashCode() + result = 31 * result + (sub?.hashCode() ?: 0) + result = 31 * result + verifiableCredential.hashCode() + result = 31 * result + (nbf?.hashCode() ?: 0) + result = 31 * result + (exp?.hashCode() ?: 0) + result = 31 * result + (jti?.hashCode() ?: 0) + result = 31 * result + (aud?.contentHashCode() ?: 0) + result = 31 * result + (originalJWTString?.hashCode() ?: 0) + return result + } } inline fun JsonObject.getCredentialField(name: String, isOptional: Boolean = false): T { diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/JWTCredential.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/JWTCredential.kt index 5db938137..1420a34a7 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/JWTCredential.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/JWTCredential.kt @@ -19,10 +19,9 @@ data class JWTCredential(val data: String) : Credential { val credentialString = jwtParts[1] val base64Data = Base64.getUrlDecoder().decode(credentialString) val jsonString = base64Data.toString(Charsets.UTF_8) - val dataValue = jsonString.toByteArray(Charsets.UTF_8) val json = Json { ignoreUnknownKeys = true } - this.jwtPayload = json.decodeFromString(dataValue.decodeToString()) + this.jwtPayload = json.decodeFromString(jsonString) } override val id: String diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/PolluxImpl.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/PolluxImpl.kt index cc6346e33..3fc0e3e73 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/PolluxImpl.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/PolluxImpl.kt @@ -39,7 +39,7 @@ class PolluxImpl(val castor: Castor) : Pollux { try { Json.decodeFromString(data) } catch (e: Exception) { - throw PolluxError.InvalidCredentialError() + throw PolluxError.InvalidCredentialError(cause = e.cause) } } } diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/PrismAgent.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/PrismAgent.kt index 9aadec5c1..d74bbdf54 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/PrismAgent.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/PrismAgent.kt @@ -532,7 +532,7 @@ class PrismAgent { val jwtString = pollux.createRequestCredentialJWT(did, privateKey, offerJsonObject) val attachmentDescriptor = AttachmentDescriptor( - mediaType = "prism/jwt", + mediaType = JWT_MEDIA_TYPE, data = AttachmentBase64(jwtString.base64UrlEncoded) ) return RequestCredential(