diff --git a/Sources/Auth/Services/App/AuthRespondSubscriber.swift b/Sources/Auth/Services/App/AuthRespondSubscriber.swift new file mode 100644 index 000000000..a6f1f8b57 --- /dev/null +++ b/Sources/Auth/Services/App/AuthRespondSubscriber.swift @@ -0,0 +1,39 @@ +import Combine +import Foundation +import WalletConnectUtils +import JSONRPC + +class AuthRespondSubscriber { + private let networkingInteractor: NetworkInteracting + private let logger: ConsoleLogging + private let rpcHistory: RPCHistory + private var publishers = [AnyCancellable]() + var onResponse: ((_ id: RPCID, _ cacao: Cacao) -> Void)? + + init(networkingInteractor: NetworkInteracting, + logger: ConsoleLogging, + rpcHistory: RPCHistory) { + self.networkingInteractor = networkingInteractor + self.logger = logger + self.rpcHistory = rpcHistory + subscribeForResponse() + } + + private func subscribeForResponse() { + networkingInteractor.responsePublisher.sink { [unowned self] subscriptionPayload in + guard let request = rpcHistory.get(recordId: subscriptionPayload.request.id!)?.request, + request.method == "wc_authRequest" else { return } + networkingInteractor.unsubscribe(topic: subscriptionPayload.topic) + guard let cacao = try? subscriptionPayload.request.result?.get(Cacao.self) else { + logger.debug("Malformed auth response params") + return + } + do { + try CacaoSignatureVerifier().verifySignature(cacao) + onResponse?(subscriptionPayload.request.id!, cacao) + } catch { + logger.debug("Received response with invalid signature") + } + }.store(in: &publishers) + } +} diff --git a/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift b/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift new file mode 100644 index 000000000..64f70488b --- /dev/null +++ b/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift @@ -0,0 +1,15 @@ +import Foundation + +protocol CacaoSignatureVerifying { + func verifySignature(_ cacao: Cacao) throws +} + +class CacaoSignatureVerifier: CacaoSignatureVerifying { + enum Errors: Error { + case signatureInvalid + } + + func verifySignature(_ cacao: Cacao) throws { + fatalError("not implemented") + } +} diff --git a/Sources/Auth/Services/Common/NetworkingInteractor.swift b/Sources/Auth/Services/Common/NetworkingInteractor.swift index c81420b26..fd480db62 100644 --- a/Sources/Auth/Services/Common/NetworkingInteractor.swift +++ b/Sources/Auth/Services/Common/NetworkingInteractor.swift @@ -7,7 +7,9 @@ import JSONRPC protocol NetworkInteracting { var requestPublisher: AnyPublisher {get} + var responsePublisher: AnyPublisher {get} func subscribe(topic: String) async throws + func unsubscribe(topic: String) func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws func respond(topic: String, response: RPCResponse, tag: Int, envelopeType: Envelope.EnvelopeType) async throws } @@ -26,6 +28,10 @@ class NetworkingInteractor: NetworkInteracting { requestPublisherSubject.eraseToAnyPublisher() } private let requestPublisherSubject = PassthroughSubject() + var responsePublisher: AnyPublisher { + responsePublisherSubject.eraseToAnyPublisher() + } + private let responsePublisherSubject = PassthroughSubject() init(relayClient: RelayClient, serializer: Serializing, @@ -39,6 +45,10 @@ class NetworkingInteractor: NetworkInteracting { try await relayClient.subscribe(topic: topic) } + func unsubscribe(topic: String) { + fatalError("not implemented") + } + func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) let message = try! serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType) diff --git a/Sources/Auth/Types/Cacao/Cacao.swift b/Sources/Auth/Types/Cacao/Cacao.swift index 95a2d59c5..80e7ce8b0 100644 --- a/Sources/Auth/Types/Cacao/Cacao.swift +++ b/Sources/Auth/Types/Cacao/Cacao.swift @@ -1,6 +1,6 @@ import Foundation -struct Cacao: Codable, Equatable { +public struct Cacao: Codable, Equatable { let header: CacaoHeader let payload: CacaoPayload let signature: CacaoSignature diff --git a/Sources/Auth/Types/RequestSubscriptionPayload.swift b/Sources/Auth/Types/RequestSubscriptionPayload.swift index b7865d715..00a5cdcb4 100644 --- a/Sources/Auth/Types/RequestSubscriptionPayload.swift +++ b/Sources/Auth/Types/RequestSubscriptionPayload.swift @@ -1,7 +1,7 @@ import Foundation import JSONRPC -struct RequestSubscriptionPayload: Codable { - let id: Int64 +struct RequestSubscriptionPayload: Codable, Equatable { + let topic: String let request: RPCRequest } diff --git a/Sources/Auth/Types/ResponseSubscriptionPayload.swift b/Sources/Auth/Types/ResponseSubscriptionPayload.swift new file mode 100644 index 000000000..94c77ff86 --- /dev/null +++ b/Sources/Auth/Types/ResponseSubscriptionPayload.swift @@ -0,0 +1,7 @@ +import Foundation +import JSONRPC + +struct ResponseSubscriptionPayload: Codable, Equatable { + let topic: String + let request: RPCResponse +} diff --git a/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift b/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift index 32e8abe85..2ccd19b84 100644 --- a/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift +++ b/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift @@ -6,6 +6,11 @@ import WalletConnectKMS struct NetworkingInteractorMock: NetworkInteracting { + var responsePublisher: AnyPublisher { + responsePublisherSubject.eraseToAnyPublisher() + } + private let responsePublisherSubject = PassthroughSubject() + let requestPublisherSubject = PassthroughSubject() var requestPublisher: AnyPublisher { requestPublisherSubject.eraseToAnyPublisher() @@ -15,6 +20,10 @@ struct NetworkingInteractorMock: NetworkInteracting { } + func unsubscribe(topic: String) { + + } + func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { }