From 507bf1a8e89b6b5f60ad6dff4332c8197e8eef4e Mon Sep 17 00:00:00 2001 From: dtsiflit Date: Thu, 12 Dec 2024 10:17:15 +0200 Subject: [PATCH 1/4] [fix] metadata claims support for sd-jwt-vc --- .../CredentialIssuanceRequest.swift | 6 +-- Sources/Entities/Profiles/MsoMdocFormat.swift | 10 ++--- Sources/Entities/Profiles/SdJwtVcFormat.swift | 38 ++++++++++++++++++- .../Issuance/IssuanceAuthorizationTest.swift | 1 + 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/Sources/Entities/IssuanceFlows/CredentialIssuanceRequest.swift b/Sources/Entities/IssuanceFlows/CredentialIssuanceRequest.swift index aed2884..8a3ea01 100644 --- a/Sources/Entities/IssuanceFlows/CredentialIssuanceRequest.swift +++ b/Sources/Entities/IssuanceFlows/CredentialIssuanceRequest.swift @@ -26,10 +26,10 @@ public let FORMAT_W3C_SIGNED_JWT = "jwt_vc_json" public typealias Namespace = String public typealias ClaimName = String -public typealias MsoMdocClaims = [Namespace: [ClaimName: Claim]] -public extension MsoMdocClaims { +public typealias MetadataClaims = [Namespace: [ClaimName: Claim]] +public extension MetadataClaims { init(json: JSON) { - var claims = MsoMdocClaims() + var claims = MetadataClaims() if let _ = json.dictionaryObject { for (namespace, subJSON) in json.dictionaryValue { var namespaceClaims = [ClaimName: Claim]() diff --git a/Sources/Entities/Profiles/MsoMdocFormat.swift b/Sources/Entities/Profiles/MsoMdocFormat.swift index b1d6fc3..3252ef2 100644 --- a/Sources/Entities/Profiles/MsoMdocFormat.swift +++ b/Sources/Entities/Profiles/MsoMdocFormat.swift @@ -192,7 +192,7 @@ public extension MsoMdocFormat { let display: [Display] = self.display ?? [] let credentialSigningAlgValuesSupported: [String] = self.credentialSigningAlgValuesSupported ?? [] - let claims: MsoMdocClaims = claims?.mapValues { namespaceAndClaims in + let claims: MetadataClaims = claims?.mapValues { namespaceAndClaims in namespaceAndClaims.mapValues { claim in Claim( mandatory: claim.mandatory ?? false, @@ -229,7 +229,7 @@ public extension MsoMdocFormat { public let proofTypesSupported: [String: ProofSigningAlgorithmsSupported]? public let display: [Display] public let docType: String - public let claims: MsoMdocClaims + public let claims: MetadataClaims public let order: [ClaimName] var claimList: [String] { @@ -256,7 +256,7 @@ public extension MsoMdocFormat { proofTypesSupported: [String: ProofSigningAlgorithmsSupported]?, display: [Display], docType: String, - claims: MsoMdocClaims, + claims: MetadataClaims, order: [ClaimName] ) { self.format = format @@ -283,7 +283,7 @@ public extension MsoMdocFormat { display = try container.decode([Display].self, forKey: .display) docType = try container.decode(String.self, forKey: .docType) - claims = try container.decode(MsoMdocClaims.self, forKey: .claims) + claims = try container.decode(MetadataClaims.self, forKey: .claims) order = try container.decode([ClaimName].self, forKey: .order) } @@ -323,7 +323,7 @@ public extension MsoMdocFormat { Display(json: json) } self.docType = json["doctype"].stringValue - self.claims = MsoMdocClaims(json: json["claims"]) + self.claims = MetadataClaims(json: json["claims"]) self.order = json["order"].arrayValue.map { ClaimName($0.stringValue) } diff --git a/Sources/Entities/Profiles/SdJwtVcFormat.swift b/Sources/Entities/Profiles/SdJwtVcFormat.swift index 948593c..35bc41b 100644 --- a/Sources/Entities/Profiles/SdJwtVcFormat.swift +++ b/Sources/Entities/Profiles/SdJwtVcFormat.swift @@ -45,6 +45,7 @@ public extension SdJwtVcFormat { public let credentialEncryptionKey: SecKey? public let credentialResponseEncryptionAlg: JWEAlgorithm? public let credentialResponseEncryptionMethod: JOSEEncryptionMethod? + public let claimSet: ClaimSet? public let credentialDefinition: CredentialDefinition public let requestedCredentialResponseEncryption: RequestedCredentialResponseEncryption public let credentialIdentifier: CredentialIdentifier? @@ -54,6 +55,7 @@ public extension SdJwtVcFormat { case credentialEncryptionJwk case credentialResponseEncryptionAlg case credentialResponseEncryptionMethod + case claimSet case credentialDefinition case credentialIdentifier } @@ -65,6 +67,7 @@ public extension SdJwtVcFormat { credentialEncryptionKey: SecKey? = nil, credentialResponseEncryptionAlg: JWEAlgorithm? = nil, credentialResponseEncryptionMethod: JOSEEncryptionMethod? = nil, + claimSet: ClaimSet? = nil, credentialDefinition: CredentialDefinition, credentialIdentifier: CredentialIdentifier? ) throws { @@ -86,6 +89,7 @@ public extension SdJwtVcFormat { responseEncryptionAlg: credentialResponseEncryptionAlg, responseEncryptionMethod: credentialResponseEncryptionMethod ) + self.claimSet = claimSet } public init(from decoder: Decoder) throws { @@ -106,6 +110,7 @@ public extension SdJwtVcFormat { try container.encode(credentialResponseEncryptionMethod, forKey: .credentialResponseEncryptionMethod) try container.encode(credentialDefinition, forKey: .credentialDefinition) + try container.encode(claimSet, forKey: .claimSet) } public struct CredentialDefinition: Codable { @@ -178,6 +183,7 @@ public extension SdJwtVcFormat { public let proofTypesSupported: [String: ProofSigningAlgorithmsSupported]? public let display: [Display]? public let credentialDefinition: CredentialDefinitionTO + public let claims: [String: [String: Claim]]? enum CodingKeys: String, CodingKey { case format @@ -188,6 +194,7 @@ public extension SdJwtVcFormat { case proofTypesSupported = "proof_types_supported" case display case credentialDefinition = "credential_definition" + case claims } public init( @@ -198,6 +205,7 @@ public extension SdJwtVcFormat { credentialSigningAlgValuesSupported: [String]? = nil, proofTypesSupported: [String: ProofSigningAlgorithmsSupported]? = nil, display: [Display]? = nil, + claims: [String : [String : Claim]]? = nil, credentialDefinition: CredentialDefinitionTO ) { self.format = format @@ -208,6 +216,7 @@ public extension SdJwtVcFormat { self.proofTypesSupported = proofTypesSupported self.display = display self.credentialDefinition = credentialDefinition + self.claims = claims } func toDomain() throws -> SdJwtVcFormat.CredentialConfiguration { @@ -220,6 +229,21 @@ public extension SdJwtVcFormat { let credentialSigningAlgValuesSupported: [String] = self.credentialSigningAlgValuesSupported ?? [] let credentialDefinition = self.credentialDefinition.toDomain() + let claims: MetadataClaims = claims?.mapValues { namespaceAndClaims in + namespaceAndClaims.mapValues { claim in + Claim( + mandatory: claim.mandatory ?? false, + valueType: claim.valueType, + display: claim.display?.compactMap { + Display( + name: $0.name, + locale: $0.locale + ) + } + ) + } + } ?? [:] + return .init( scope: scope, vct: vct, @@ -227,7 +251,8 @@ public extension SdJwtVcFormat { credentialSigningAlgValuesSupported: credentialSigningAlgValuesSupported, proofTypesSupported: self.proofTypesSupported, display: display, - credentialDefinition: credentialDefinition + credentialDefinition: credentialDefinition, + claims: claims ) } } @@ -239,6 +264,7 @@ public extension SdJwtVcFormat { public let credentialSigningAlgValuesSupported: [String] public let proofTypesSupported: [String: ProofSigningAlgorithmsSupported]? public let display: [Display] + public let claims: MetadataClaims public let credentialDefinition: CredentialDefinition enum CodingKeys: String, CodingKey { @@ -249,6 +275,7 @@ public extension SdJwtVcFormat { case proofTypesSupported = "proof_types_supported" case display case credentialDefinition = "credential_definition" + case claims } var claimList: [String] { @@ -262,7 +289,8 @@ public extension SdJwtVcFormat { credentialSigningAlgValuesSupported: [String], proofTypesSupported: [String: ProofSigningAlgorithmsSupported]?, display: [Display], - credentialDefinition: CredentialDefinition + credentialDefinition: CredentialDefinition, + claims: MetadataClaims ) { self.scope = scope self.vct = vct @@ -271,6 +299,7 @@ public extension SdJwtVcFormat { self.proofTypesSupported = proofTypesSupported self.display = display self.credentialDefinition = credentialDefinition + self.claims = claims } public init(from decoder: Decoder) throws { @@ -285,6 +314,7 @@ public extension SdJwtVcFormat { display = try container.decode([Display].self, forKey: .display) credentialDefinition = try container.decode(CredentialDefinition.self, forKey: .credentialDefinition) + claims = try container.decode(MetadataClaims.self, forKey: .claims) } public func encode(to encoder: Encoder) throws { @@ -295,6 +325,7 @@ public extension SdJwtVcFormat { try container.encode(proofTypesSupported, forKey: .proofTypesSupported) try container.encode(display, forKey: .display) try container.encode(credentialDefinition, forKey: .credentialDefinition) + try container.encode(claims, forKey: .claims) } init(json: JSON) throws { @@ -318,7 +349,10 @@ public extension SdJwtVcFormat { self.display = json["display"].arrayValue.map { json in Display(json: json) } + self.credentialDefinition = CredentialDefinition(json: json["credential_definition"]) + + self.claims = MetadataClaims(json: json["claims"]) } func toIssuanceRequest( diff --git a/Tests/Issuance/IssuanceAuthorizationTest.swift b/Tests/Issuance/IssuanceAuthorizationTest.swift index b1bee58..389dd9e 100644 --- a/Tests/Issuance/IssuanceAuthorizationTest.swift +++ b/Tests/Issuance/IssuanceAuthorizationTest.swift @@ -333,6 +333,7 @@ class IssuanceAuthorizationTest: XCTestCase { /// Submit /// let urlString = """ + https://trial.authlete.net/api/offer/Kb83yMR1fAqBmwr2SmCbuVdKMYAhYrv5uMl-_TrCEZ0 """ if urlString.isEmpty { From ab620c591f8428780b655acc42d703f6bf111da8 Mon Sep 17 00:00:00 2001 From: dtsiflit Date: Thu, 12 Dec 2024 11:02:31 +0200 Subject: [PATCH 2/4] [fix] sd jwt cv claims metadata fixes --- .../CredentialIssuanceRequest.swift | 36 +++++++++++++++++-- Sources/Entities/Profiles/MsoMdocFormat.swift | 10 +++--- Sources/Entities/Profiles/SdJwtVcFormat.swift | 16 ++++----- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/Sources/Entities/IssuanceFlows/CredentialIssuanceRequest.swift b/Sources/Entities/IssuanceFlows/CredentialIssuanceRequest.swift index 8a3ea01..f993fb4 100644 --- a/Sources/Entities/IssuanceFlows/CredentialIssuanceRequest.swift +++ b/Sources/Entities/IssuanceFlows/CredentialIssuanceRequest.swift @@ -26,10 +26,10 @@ public let FORMAT_W3C_SIGNED_JWT = "jwt_vc_json" public typealias Namespace = String public typealias ClaimName = String -public typealias MetadataClaims = [Namespace: [ClaimName: Claim]] -public extension MetadataClaims { +public typealias MsoMdocMetadataClaims = [Namespace: [ClaimName: Claim]] +public extension MsoMdocMetadataClaims { init(json: JSON) { - var claims = MetadataClaims() + var claims = MsoMdocMetadataClaims() if let _ = json.dictionaryObject { for (namespace, subJSON) in json.dictionaryValue { var namespaceClaims = [ClaimName: Claim]() @@ -58,6 +58,36 @@ public extension MetadataClaims { } } +public typealias SdJwtVCMetadataClaims = [ClaimName: Claim] +public extension SdJwtVCMetadataClaims { + init(json: JSON) { + var claims = SdJwtVCMetadataClaims() + if let _ = json.dictionaryObject { + var namespaceClaims = [ClaimName: Claim]() + for (claimName, claimJSON) in json.dictionaryValue { + let claim = Claim( + mandatory: claimJSON["mandatory"].bool, + valueType: claimJSON["valuetype"].string, + display: claimJSON["display"].arrayValue.compactMap { + Display(json: $0) + } + ) + namespaceClaims[claimName] = claim + } + claims = namespaceClaims + } else if let jsonArray = json.arrayObject { + for element in jsonArray { + if let dictionary = element as? [String: String] { + if let key = dictionary.keys.first { + claims[key] = Claim() + } + } + } + } + self = claims + } +} + public enum CredentialIssuanceRequest { case single(SingleCredential, IssuanceResponseEncryptionSpec?) } diff --git a/Sources/Entities/Profiles/MsoMdocFormat.swift b/Sources/Entities/Profiles/MsoMdocFormat.swift index 3252ef2..9ef2bb9 100644 --- a/Sources/Entities/Profiles/MsoMdocFormat.swift +++ b/Sources/Entities/Profiles/MsoMdocFormat.swift @@ -192,7 +192,7 @@ public extension MsoMdocFormat { let display: [Display] = self.display ?? [] let credentialSigningAlgValuesSupported: [String] = self.credentialSigningAlgValuesSupported ?? [] - let claims: MetadataClaims = claims?.mapValues { namespaceAndClaims in + let claims: MsoMdocMetadataClaims = claims?.mapValues { namespaceAndClaims in namespaceAndClaims.mapValues { claim in Claim( mandatory: claim.mandatory ?? false, @@ -229,7 +229,7 @@ public extension MsoMdocFormat { public let proofTypesSupported: [String: ProofSigningAlgorithmsSupported]? public let display: [Display] public let docType: String - public let claims: MetadataClaims + public let claims: MsoMdocMetadataClaims public let order: [ClaimName] var claimList: [String] { @@ -256,7 +256,7 @@ public extension MsoMdocFormat { proofTypesSupported: [String: ProofSigningAlgorithmsSupported]?, display: [Display], docType: String, - claims: MetadataClaims, + claims: MsoMdocMetadataClaims, order: [ClaimName] ) { self.format = format @@ -283,7 +283,7 @@ public extension MsoMdocFormat { display = try container.decode([Display].self, forKey: .display) docType = try container.decode(String.self, forKey: .docType) - claims = try container.decode(MetadataClaims.self, forKey: .claims) + claims = try container.decode(MsoMdocMetadataClaims.self, forKey: .claims) order = try container.decode([ClaimName].self, forKey: .order) } @@ -323,7 +323,7 @@ public extension MsoMdocFormat { Display(json: json) } self.docType = json["doctype"].stringValue - self.claims = MetadataClaims(json: json["claims"]) + self.claims = MsoMdocMetadataClaims(json: json["claims"]) self.order = json["order"].arrayValue.map { ClaimName($0.stringValue) } diff --git a/Sources/Entities/Profiles/SdJwtVcFormat.swift b/Sources/Entities/Profiles/SdJwtVcFormat.swift index 35bc41b..3d9525a 100644 --- a/Sources/Entities/Profiles/SdJwtVcFormat.swift +++ b/Sources/Entities/Profiles/SdJwtVcFormat.swift @@ -183,7 +183,7 @@ public extension SdJwtVcFormat { public let proofTypesSupported: [String: ProofSigningAlgorithmsSupported]? public let display: [Display]? public let credentialDefinition: CredentialDefinitionTO - public let claims: [String: [String: Claim]]? + public let claims: [String: Claim]? enum CodingKeys: String, CodingKey { case format @@ -205,7 +205,7 @@ public extension SdJwtVcFormat { credentialSigningAlgValuesSupported: [String]? = nil, proofTypesSupported: [String: ProofSigningAlgorithmsSupported]? = nil, display: [Display]? = nil, - claims: [String : [String : Claim]]? = nil, + claims: [String : Claim]? = nil, credentialDefinition: CredentialDefinitionTO ) { self.format = format @@ -229,8 +229,7 @@ public extension SdJwtVcFormat { let credentialSigningAlgValuesSupported: [String] = self.credentialSigningAlgValuesSupported ?? [] let credentialDefinition = self.credentialDefinition.toDomain() - let claims: MetadataClaims = claims?.mapValues { namespaceAndClaims in - namespaceAndClaims.mapValues { claim in + let claims: SdJwtVCMetadataClaims = claims?.mapValues { claim in Claim( mandatory: claim.mandatory ?? false, valueType: claim.valueType, @@ -241,7 +240,6 @@ public extension SdJwtVcFormat { ) } ) - } } ?? [:] return .init( @@ -264,7 +262,7 @@ public extension SdJwtVcFormat { public let credentialSigningAlgValuesSupported: [String] public let proofTypesSupported: [String: ProofSigningAlgorithmsSupported]? public let display: [Display] - public let claims: MetadataClaims + public let claims: SdJwtVCMetadataClaims public let credentialDefinition: CredentialDefinition enum CodingKeys: String, CodingKey { @@ -290,7 +288,7 @@ public extension SdJwtVcFormat { proofTypesSupported: [String: ProofSigningAlgorithmsSupported]?, display: [Display], credentialDefinition: CredentialDefinition, - claims: MetadataClaims + claims: SdJwtVCMetadataClaims ) { self.scope = scope self.vct = vct @@ -314,7 +312,7 @@ public extension SdJwtVcFormat { display = try container.decode([Display].self, forKey: .display) credentialDefinition = try container.decode(CredentialDefinition.self, forKey: .credentialDefinition) - claims = try container.decode(MetadataClaims.self, forKey: .claims) + claims = try container.decode(SdJwtVCMetadataClaims.self, forKey: .claims) } public func encode(to encoder: Encoder) throws { @@ -352,7 +350,7 @@ public extension SdJwtVcFormat { self.credentialDefinition = CredentialDefinition(json: json["credential_definition"]) - self.claims = MetadataClaims(json: json["claims"]) + self.claims = SdJwtVCMetadataClaims(json: json["claims"]) } func toIssuanceRequest( From 94e466b9efdd9e2d4661f9e0e34ac7d35a8348e4 Mon Sep 17 00:00:00 2001 From: dtsiflit Date: Thu, 12 Dec 2024 11:05:13 +0200 Subject: [PATCH 3/4] [fix] renamed msomdoc typealias --- .../IssuanceFlows/CredentialIssuanceRequest.swift | 6 +++--- Sources/Entities/Profiles/MsoMdocFormat.swift | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/Entities/IssuanceFlows/CredentialIssuanceRequest.swift b/Sources/Entities/IssuanceFlows/CredentialIssuanceRequest.swift index f993fb4..afbe87c 100644 --- a/Sources/Entities/IssuanceFlows/CredentialIssuanceRequest.swift +++ b/Sources/Entities/IssuanceFlows/CredentialIssuanceRequest.swift @@ -26,10 +26,10 @@ public let FORMAT_W3C_SIGNED_JWT = "jwt_vc_json" public typealias Namespace = String public typealias ClaimName = String -public typealias MsoMdocMetadataClaims = [Namespace: [ClaimName: Claim]] -public extension MsoMdocMetadataClaims { +public typealias MsoMdocClaims = [Namespace: [ClaimName: Claim]] +public extension MsoMdocClaims { init(json: JSON) { - var claims = MsoMdocMetadataClaims() + var claims = MsoMdocClaims() if let _ = json.dictionaryObject { for (namespace, subJSON) in json.dictionaryValue { var namespaceClaims = [ClaimName: Claim]() diff --git a/Sources/Entities/Profiles/MsoMdocFormat.swift b/Sources/Entities/Profiles/MsoMdocFormat.swift index 9ef2bb9..b1d6fc3 100644 --- a/Sources/Entities/Profiles/MsoMdocFormat.swift +++ b/Sources/Entities/Profiles/MsoMdocFormat.swift @@ -192,7 +192,7 @@ public extension MsoMdocFormat { let display: [Display] = self.display ?? [] let credentialSigningAlgValuesSupported: [String] = self.credentialSigningAlgValuesSupported ?? [] - let claims: MsoMdocMetadataClaims = claims?.mapValues { namespaceAndClaims in + let claims: MsoMdocClaims = claims?.mapValues { namespaceAndClaims in namespaceAndClaims.mapValues { claim in Claim( mandatory: claim.mandatory ?? false, @@ -229,7 +229,7 @@ public extension MsoMdocFormat { public let proofTypesSupported: [String: ProofSigningAlgorithmsSupported]? public let display: [Display] public let docType: String - public let claims: MsoMdocMetadataClaims + public let claims: MsoMdocClaims public let order: [ClaimName] var claimList: [String] { @@ -256,7 +256,7 @@ public extension MsoMdocFormat { proofTypesSupported: [String: ProofSigningAlgorithmsSupported]?, display: [Display], docType: String, - claims: MsoMdocMetadataClaims, + claims: MsoMdocClaims, order: [ClaimName] ) { self.format = format @@ -283,7 +283,7 @@ public extension MsoMdocFormat { display = try container.decode([Display].self, forKey: .display) docType = try container.decode(String.self, forKey: .docType) - claims = try container.decode(MsoMdocMetadataClaims.self, forKey: .claims) + claims = try container.decode(MsoMdocClaims.self, forKey: .claims) order = try container.decode([ClaimName].self, forKey: .order) } @@ -323,7 +323,7 @@ public extension MsoMdocFormat { Display(json: json) } self.docType = json["doctype"].stringValue - self.claims = MsoMdocMetadataClaims(json: json["claims"]) + self.claims = MsoMdocClaims(json: json["claims"]) self.order = json["order"].arrayValue.map { ClaimName($0.stringValue) } From 69f2f68be7206b3465108dca5f76005388b242fa Mon Sep 17 00:00:00 2001 From: dtsiflit Date: Thu, 12 Dec 2024 11:06:23 +0200 Subject: [PATCH 4/4] [fix] test fix --- Tests/Issuance/IssuanceAuthorizationTest.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/Issuance/IssuanceAuthorizationTest.swift b/Tests/Issuance/IssuanceAuthorizationTest.swift index 389dd9e..b1bee58 100644 --- a/Tests/Issuance/IssuanceAuthorizationTest.swift +++ b/Tests/Issuance/IssuanceAuthorizationTest.swift @@ -333,7 +333,6 @@ class IssuanceAuthorizationTest: XCTestCase { /// Submit /// let urlString = """ - https://trial.authlete.net/api/offer/Kb83yMR1fAqBmwr2SmCbuVdKMYAhYrv5uMl-_TrCEZ0 """ if urlString.isEmpty {