From 68ffab82843d8e95dd87a688d3e7e9ed50969cc0 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 3 Aug 2022 14:12:48 +0200 Subject: [PATCH 1/3] Add Auth Respond Subscriber --- .../Services/App/AuthRespondSubscriber.swift | 40 +++++++++++++++++++ .../Auth/Services/Common/CacaoFormatter.swift | 1 + .../Common/CacaoSignatureVerifier.swift | 16 ++++++++ .../Common/NetworkingInteractor.swift | 6 +++ Sources/Auth/Types/Cacao/Cacao.swift | 2 +- .../Mocks/NetworkingInteractorMock.swift | 2 + 6 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 Sources/Auth/Services/App/AuthRespondSubscriber.swift create mode 100644 Sources/Auth/Services/Common/CacaoSignatureVerifier.swift diff --git a/Sources/Auth/Services/App/AuthRespondSubscriber.swift b/Sources/Auth/Services/App/AuthRespondSubscriber.swift new file mode 100644 index 000000000..e3379f771 --- /dev/null +++ b/Sources/Auth/Services/App/AuthRespondSubscriber.swift @@ -0,0 +1,40 @@ +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] response in + guard let request = rpcHistory.get(recordId: response.id!)?.request, + request.method == "wc_authRequest" else { return } + + guard let cacao = try? response.result?.get(Cacao.self) else { + logger.debug("Malformed auth response params") + return + } + do { + try CacaoSignatureVerifier().verifySignature(cacao) + onResponse?(response.id!, cacao) + } catch { + logger.debug("Received response with invalid signature") + } + }.store(in: &publishers) + } +} + diff --git a/Sources/Auth/Services/Common/CacaoFormatter.swift b/Sources/Auth/Services/Common/CacaoFormatter.swift index 7daf6b3d1..43ddb9058 100644 --- a/Sources/Auth/Services/Common/CacaoFormatter.swift +++ b/Sources/Auth/Services/Common/CacaoFormatter.swift @@ -1,4 +1,5 @@ import Foundation +import WalletConnectUtils protocol CacaoFormatting { func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ issuer: Account) -> Cacao diff --git a/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift b/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift new file mode 100644 index 000000000..76a6911c8 --- /dev/null +++ b/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift @@ -0,0 +1,16 @@ +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..d81e8ccfb 100644 --- a/Sources/Auth/Services/Common/NetworkingInteractor.swift +++ b/Sources/Auth/Services/Common/NetworkingInteractor.swift @@ -7,6 +7,8 @@ import JSONRPC protocol NetworkInteracting { var requestPublisher: AnyPublisher {get} + var responsePublisher: AnyPublisher {get} + func subscribe(topic: String) async throws 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 @@ -25,6 +27,10 @@ class NetworkingInteractor: NetworkInteracting { var requestPublisher: AnyPublisher { requestPublisherSubject.eraseToAnyPublisher() } + var responsePublisher: AnyPublisher { + responsePublisherSubject.eraseToAnyPublisher() + } + private let responsePublisherSubject = PassthroughSubject() private let requestPublisherSubject = PassthroughSubject() init(relayClient: RelayClient, 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/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift b/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift index 32e8abe85..d7c546725 100644 --- a/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift +++ b/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift @@ -5,6 +5,8 @@ import JSONRPC import WalletConnectKMS struct NetworkingInteractorMock: NetworkInteracting { + var responsePublisher: AnyPublisher + let requestPublisherSubject = PassthroughSubject() var requestPublisher: AnyPublisher { From 70042a5db5ce08b9bb2c7070f23db78c1fc8392e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 4 Aug 2022 09:04:18 +0200 Subject: [PATCH 2/3] Update AuthRespondSubscriber --- .../Auth/Services/App/AuthRespondSubscriber.swift | 11 +++++------ .../Auth/Services/Common/NetworkingInteractor.swift | 13 +++++++++---- Sources/Auth/Types/RequestSubscriptionPayload.swift | 4 ++-- .../Auth/Types/ResponseSubscriptionPayload.swift | 7 +++++++ .../AuthTests/Mocks/NetworkingInteractorMock.swift | 9 ++++++++- .../Stubs/RequestSubscriptionPayload.swift | 2 +- 6 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 Sources/Auth/Types/ResponseSubscriptionPayload.swift diff --git a/Sources/Auth/Services/App/AuthRespondSubscriber.swift b/Sources/Auth/Services/App/AuthRespondSubscriber.swift index e3379f771..ad8dcb7ed 100644 --- a/Sources/Auth/Services/App/AuthRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AuthRespondSubscriber.swift @@ -20,21 +20,20 @@ class AuthRespondSubscriber { } private func subscribeForResponse() { - networkingInteractor.responsePublisher.sink { [unowned self] response in - guard let request = rpcHistory.get(recordId: response.id!)?.request, + networkingInteractor.responsePublisher.sink { [unowned self] subscriptionPayload in + guard let request = rpcHistory.get(recordId: subscriptionPayload.request.id!)?.request, request.method == "wc_authRequest" else { return } - - guard let cacao = try? response.result?.get(Cacao.self) else { + 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?(response.id!, cacao) + onResponse?(subscriptionPayload.request.id!, cacao) } catch { logger.debug("Received response with invalid signature") } }.store(in: &publishers) } } - diff --git a/Sources/Auth/Services/Common/NetworkingInteractor.swift b/Sources/Auth/Services/Common/NetworkingInteractor.swift index d81e8ccfb..eb035ea26 100644 --- a/Sources/Auth/Services/Common/NetworkingInteractor.swift +++ b/Sources/Auth/Services/Common/NetworkingInteractor.swift @@ -7,9 +7,10 @@ import JSONRPC protocol NetworkInteracting { var requestPublisher: AnyPublisher {get} - var responsePublisher: 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 } @@ -27,11 +28,11 @@ class NetworkingInteractor: NetworkInteracting { var requestPublisher: AnyPublisher { requestPublisherSubject.eraseToAnyPublisher() } - var responsePublisher: AnyPublisher { + private let requestPublisherSubject = PassthroughSubject() + var responsePublisher: AnyPublisher { responsePublisherSubject.eraseToAnyPublisher() } - private let responsePublisherSubject = PassthroughSubject() - private let requestPublisherSubject = PassthroughSubject() + private let responsePublisherSubject = PassthroughSubject() init(relayClient: RelayClient, serializer: Serializing, @@ -45,6 +46,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/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 d7c546725..2ccd19b84 100644 --- a/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift +++ b/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift @@ -5,8 +5,11 @@ import JSONRPC import WalletConnectKMS struct NetworkingInteractorMock: NetworkInteracting { - var responsePublisher: AnyPublisher + var responsePublisher: AnyPublisher { + responsePublisherSubject.eraseToAnyPublisher() + } + private let responsePublisherSubject = PassthroughSubject() let requestPublisherSubject = PassthroughSubject() var requestPublisher: AnyPublisher { @@ -17,6 +20,10 @@ struct NetworkingInteractorMock: NetworkInteracting { } + func unsubscribe(topic: String) { + + } + func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { } diff --git a/Tests/AuthTests/Stubs/RequestSubscriptionPayload.swift b/Tests/AuthTests/Stubs/RequestSubscriptionPayload.swift index 0423623ec..87ddb00a5 100644 --- a/Tests/AuthTests/Stubs/RequestSubscriptionPayload.swift +++ b/Tests/AuthTests/Stubs/RequestSubscriptionPayload.swift @@ -10,6 +10,6 @@ extension RequestSubscriptionPayload { let payload = AuthPayload(requestParams: RequestParams.stub(), iat: issueAt) let params = AuthRequestParams(requester: requester, payloadParams: payload) let request = RPCRequest(method: "wc_authRequest", params: params, id: id) - return RequestSubscriptionPayload(id: 123, request: request) + return RequestSubscriptionPayload(topic: "123", request: request) } } From 0051b90152bff10a8a8fa561f4431edbacba2a03 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 4 Aug 2022 09:10:09 +0200 Subject: [PATCH 3/3] run lint --- Sources/Auth/Services/App/AuthRespondSubscriber.swift | 2 +- Sources/Auth/Services/Common/CacaoSignatureVerifier.swift | 1 - Sources/Auth/Services/Common/NetworkingInteractor.swift | 3 +-- Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Sources/Auth/Services/App/AuthRespondSubscriber.swift b/Sources/Auth/Services/App/AuthRespondSubscriber.swift index ad8dcb7ed..a6f1f8b57 100644 --- a/Sources/Auth/Services/App/AuthRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AuthRespondSubscriber.swift @@ -8,7 +8,7 @@ class AuthRespondSubscriber { private let logger: ConsoleLogging private let rpcHistory: RPCHistory private var publishers = [AnyCancellable]() - var onResponse: ((_ id: RPCID, _ cacao: Cacao)->Void)? + var onResponse: ((_ id: RPCID, _ cacao: Cacao) -> Void)? init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, diff --git a/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift b/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift index 76a6911c8..64f70488b 100644 --- a/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift +++ b/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift @@ -13,4 +13,3 @@ class CacaoSignatureVerifier: CacaoSignatureVerifying { fatalError("not implemented") } } - diff --git a/Sources/Auth/Services/Common/NetworkingInteractor.swift b/Sources/Auth/Services/Common/NetworkingInteractor.swift index eb035ea26..fd480db62 100644 --- a/Sources/Auth/Services/Common/NetworkingInteractor.swift +++ b/Sources/Auth/Services/Common/NetworkingInteractor.swift @@ -8,9 +8,8 @@ import JSONRPC protocol NetworkInteracting { var requestPublisher: AnyPublisher {get} var responsePublisher: AnyPublisher {get} - func subscribe(topic: String) async throws - func unsubscribe(topic: String) + 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 } diff --git a/Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift b/Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift index 07469153d..c78b66385 100644 --- a/Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift @@ -7,7 +7,7 @@ class AuthRequestSubscriber { private let logger: ConsoleLogging private var publishers = [AnyCancellable]() private let messageFormatter: SIWEMessageFormatting - var onRequest: ((_ id: Int64, _ message: String)->Void)? + var onRequest: ((_ id: Int64, _ message: String) -> Void)? init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging,