diff --git a/Core/Sources/Helpers/String+URL.swift b/Core/Sources/Helpers/String+URL.swift new file mode 100644 index 00000000..fc4b188b --- /dev/null +++ b/Core/Sources/Helpers/String+URL.swift @@ -0,0 +1,11 @@ +import Foundation + +public extension String { + func parseIfUrl() throws -> String { + guard self.contains("data:application/"), let url = URL(string: self) else { + return self + } + let data = try Data(contentsOf: url) + return String(data: data, encoding: .utf8) ?? self + } +} diff --git a/E2E/Tests/Source/Resolvers/PrismShortFormResolver.swift b/E2E/Tests/Source/Resolvers/PrismShortFormResolver.swift index b727a142..af52aded 100644 --- a/E2E/Tests/Source/Resolvers/PrismShortFormResolver.swift +++ b/E2E/Tests/Source/Resolvers/PrismShortFormResolver.swift @@ -36,8 +36,8 @@ class PrismShortFormResolver: DIDResolverDomain { } return DIDDocument.Service( id: service.id, - type: type, - serviceEndpoint: try convertServiceEndpoints(from: service.serviceEndpoint) + type: .many(type), + serviceEndpoint: .many(try convertServiceEndpoints(from: service.serviceEndpoint)) ) } ?? [] ) diff --git a/E2E/Tests/Source/Workflows/EdgeAgentWorkflow.swift b/E2E/Tests/Source/Workflows/EdgeAgentWorkflow.swift index 3c4e2377..07d38906 100644 --- a/E2E/Tests/Source/Workflows/EdgeAgentWorkflow.swift +++ b/E2E/Tests/Source/Workflows/EdgeAgentWorkflow.swift @@ -143,7 +143,7 @@ class EdgeAgentWorkflow { ) { sdk in let message = sdk.issueCredentialStack.removeFirst() let issuedCredential = try IssueCredential3_0(fromMessage: message) - _ = try await sdk.didcommAgent.processIssuedCredentialMessage(message: issuedCredential) + let credential = try await sdk.didcommAgent.processIssuedCredentialMessage(message: issuedCredential) try await edgeAgent.remember(key: recordId, value: message.id) } } @@ -164,11 +164,16 @@ class EdgeAgentWorkflow { ) { sdk in let credentials = sdk.didcommAgent.edgeAgent.verifiableCredentials() let credential = try await credentials.map { $0.first }.first().await() + + guard let credential else { + throw ValidationError.error(message: "No credential available to present") + } + let message = sdk.proofOfRequestStack.removeFirst() let requestPresentationMessage = try RequestPresentation(fromMessage: message) let sendProofMessage = try await sdk.didcommAgent.createPresentationForRequestProof( request: requestPresentationMessage, - credential: credential!, + credential: credential, options: [.disclosingClaims(claims: ["automation-required"])] ).makeMessage() _ = try await sdk.didcommAgent.sendMessage(message: sendProofMessage) diff --git a/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTCredential+ExportableCredential.swift b/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTCredential+ExportableCredential.swift new file mode 100644 index 00000000..e1605b20 --- /dev/null +++ b/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTCredential+ExportableCredential.swift @@ -0,0 +1,10 @@ +import Domain +import Foundation + +extension JWTCredential: ExportableCredential { + public var exporting: Data { + (try? jwtString.tryToData()) ?? Data() + } + + public var restorationType: String { "jwt" } +} diff --git a/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTCredential+StorableCredential.swift b/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTCredential+StorableCredential.swift index bec61604..8b1c7b22 100644 --- a/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTCredential+StorableCredential.swift +++ b/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTCredential+StorableCredential.swift @@ -7,7 +7,7 @@ extension JWTCredential: StorableCredential { } public var recoveryId: String { - "jwt+vc" + "jwt+credential" } public var credentialData: Data { diff --git a/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTEnvelopedVerfiablePresentation.swift b/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTEnvelopedVerfiablePresentation.swift index b717984a..057d3dbf 100644 --- a/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTEnvelopedVerfiablePresentation.swift +++ b/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTEnvelopedVerfiablePresentation.swift @@ -26,7 +26,7 @@ public struct EnvelopedVerfiablePresentation: RawCodable { enum CodingKeys: String, CodingKey { case context = "@context" case type - case verifiableCredential + case id } /// The JSON‑LD `@context` values for the enveloped presentation entry (one‑or‑many). @@ -62,7 +62,7 @@ public struct EnvelopedVerfiablePresentation: RawCodable { public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.context = try container.decodeIfPresent(OneOrMany.self, forKey: .context) ?? .many([]) - self.id = try container.decode(String.self, forKey: .context) + self.id = try container.decode(String.self, forKey: .id) self.type = try container.decodeIfPresent(OneOrMany.self, forKey: .type) ?? .many([]) self.raw = try AnyCodable(from: decoder) } @@ -71,7 +71,7 @@ public struct EnvelopedVerfiablePresentation: RawCodable { guard let raw else { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(context, forKey: .context) - try container.encode(id, forKey: .context) + try container.encode(id, forKey: .id) try container.encode(type, forKey: .type) return } diff --git a/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTEnvelopedVerifiableCredential.swift b/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTEnvelopedVerifiableCredential.swift index caa12509..190aee07 100644 --- a/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTEnvelopedVerifiableCredential.swift +++ b/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTEnvelopedVerifiableCredential.swift @@ -1,7 +1,8 @@ +import Core import Domain import Foundation -public struct JWTEnvelopedVerifiableCredential: Codable { +public struct JWTEnvelopedVerifiableCredential: RawCodable { public let iss: String? public let sub: String? public let nbf: Date? @@ -10,6 +11,7 @@ public struct JWTEnvelopedVerifiableCredential: Codable { public let jti: String? public let aud: [String]? public let vc: Credential + public let raw: AnyCodable? init( iss: String? = nil, @@ -19,7 +21,8 @@ public struct JWTEnvelopedVerifiableCredential: Codable { iat: Date? = nil, jti: String? = nil, aud: [String]? = nil, - vc: Credential + vc: Credential, + raw: AnyCodable? = nil ) { self.iss = iss self.sub = sub @@ -29,6 +32,7 @@ public struct JWTEnvelopedVerifiableCredential: Codable { self.jti = jti self.aud = aud self.vc = vc + self.raw = raw } enum CodingKeys: CodingKey { @@ -63,5 +67,22 @@ public struct JWTEnvelopedVerifiableCredential: Codable { } else { self.vc = try Credential(from: decoder) } + self.raw = try AnyCodable(from: decoder) + } + + public func encode(to encoder: any Encoder) throws { + guard let raw else { + var container = encoder.container(keyedBy: JWTEnvelopedVerifiableCredential.CodingKeys.self) + try container.encodeIfPresent(self.iss, forKey: .iss) + try container.encodeIfPresent(self.sub, forKey: .sub) + try container.encodeIfPresent(self.nbf, forKey: .nbf) + try container.encodeIfPresent(self.exp, forKey: .exp) + try container.encodeIfPresent(self.iat, forKey: .iat) + try container.encodeIfPresent(self.jti, forKey: .jti) + try container.encodeIfPresent(self.aud, forKey: .aud) + try container.encode(self.vc, forKey: .vc) + return + } + try raw.encode(to: encoder) } } diff --git a/EdgeAgentSDK/Pollux/Sources/Models/AnonCreds/JWTCredential+ExportableCredential.swift b/EdgeAgentSDK/Pollux/Sources/Models/JWT/Legacy/LegacyJWTCredential+ExportableCredential.swift similarity index 100% rename from EdgeAgentSDK/Pollux/Sources/Models/AnonCreds/JWTCredential+ExportableCredential.swift rename to EdgeAgentSDK/Pollux/Sources/Models/JWT/Legacy/LegacyJWTCredential+ExportableCredential.swift diff --git a/EdgeAgentSDK/Pollux/Sources/Models/JWT/Presentation/JWTCreatePresentation.swift b/EdgeAgentSDK/Pollux/Sources/Models/JWT/Presentation/JWTCreatePresentation.swift index 107abc45..ca67ade2 100644 --- a/EdgeAgentSDK/Pollux/Sources/Models/JWT/Presentation/JWTCreatePresentation.swift +++ b/EdgeAgentSDK/Pollux/Sources/Models/JWT/Presentation/JWTCreatePresentation.swift @@ -76,7 +76,7 @@ struct JWTCreatePresentation { throw PolluxError.credentialIsNotOfPresentationDefinitionRequiredAlgorithm } - let credentialSubject = try JSONEncoder().encode(credential.defaultEnvelop.vc) + let credentialSubject = try JSONEncoder().encode(credential.defaultEnvelop) try presentationRequest.presentationDefinition.inputDescriptors.forEach { try $0.constraints.fields.forEach { @@ -92,7 +92,7 @@ struct JWTCreatePresentation { format: "jwt", pathNested: .init( id: $0.id, - path: "$.vp.verifiableCredential[0]", + path: "$.vp.verifiableCredential[0].id", format: "jwt" ) ) @@ -126,7 +126,7 @@ struct JWTCreatePresentation { credential: JWTCredential, request: Data, did: DID - ) throws -> JWTEnvelopedVerifiablePresentation> { + ) throws -> JWTEnvelopedVerifiablePresentation>> { let jsonObject = try JSONSerialization.jsonObject(with: request) guard let domain = findValue(forKey: "domain", in: jsonObject), @@ -140,17 +140,17 @@ struct JWTCreatePresentation { vp: VerifiablePresentation( context: .one(W3CRegisteredConstants.verifiableCredential2_0Context), type: .one(W3CRegisteredConstants.verifiablePresentationType), - verifiableCredential: EnvelopedVerfiablePresentation( + verifiableCredential: .many([EnvelopedVerfiablePresentation( context: .one(W3CRegisteredConstants.verifiableCredential2_0Context), id: "data:application/vc+jwt,\(credential.jwtString)", type: .one(W3CRegisteredConstants.envelopedVerifiableCredentialType) - ) + )]) ) ) } - private func vcPresentationJWTString( - payload: JWTEnvelopedVerifiablePresentation>, + private func vcPresentationJWTString( + payload: Payload, exportableKey: ExportableKey ) throws -> String { let keyJWK = exportableKey.jwk diff --git a/EdgeAgentSDK/Pollux/Sources/Operation/JWT/VerifyJWT.swift b/EdgeAgentSDK/Pollux/Sources/Operation/JWT/VerifyJWT.swift index fdef95e0..37ade22d 100644 --- a/EdgeAgentSDK/Pollux/Sources/Operation/JWT/VerifyJWT.swift +++ b/EdgeAgentSDK/Pollux/Sources/Operation/JWT/VerifyJWT.swift @@ -1,3 +1,4 @@ +import Core import Domain import Foundation import JSONWebAlgorithms @@ -7,6 +8,7 @@ struct VerifyJWT { let castor: Castor func verifyJWT(jwtString: String) async throws -> Bool { + let jwtString = try jwtString.parseIfUrl() try await verifyJWTCredentialRevocation(jwtString: jwtString) let payload: DefaultJWTClaimsImpl = try JWT.getPayload(jwtString: jwtString) guard let issuer = payload.iss else { diff --git a/EdgeAgentSDK/Pollux/Sources/PolluxImpl+Public.swift b/EdgeAgentSDK/Pollux/Sources/PolluxImpl+Public.swift index 4efb5be6..834b7944 100644 --- a/EdgeAgentSDK/Pollux/Sources/PolluxImpl+Public.swift +++ b/EdgeAgentSDK/Pollux/Sources/PolluxImpl+Public.swift @@ -8,8 +8,12 @@ extension PolluxImpl: Pollux { switch restorationIdentifier { case "sd-jwt+credential": return try SDJWTCredential(sdjwtString: credentialData.tryToString()) - case "jwt+credential": - return try JSONDecoder().decode(LegacyJWTCredential.self, from: credentialData) + case "jwt+credential", "jwt+vc": + do { + return try JWTCredential(jwtString: credentialData.tryToString()) + } catch { + return try JSONDecoder().decode(LegacyJWTCredential.self, from: credentialData) + } case "w3c+credential": return try JSONDecoder().decode(LegacyW3CVerifiableCredential.self, from: credentialData) case "anon+credential":