diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 8e343268f..731d74b14 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -69,7 +69,7 @@ final class AuthTests: XCTestCase { Task(priority: .high) { let signature = try! MessageSigner().sign(message: message, privateKey: prvKey) let cacaoSignature = CacaoSignature(t: "eip191", s: signature) - try! await wallet.respond(requestId: id, result: .success(cacaoSignature)) + try! await wallet.respond(requestId: id, signature: cacaoSignature) } } .store(in: &publishers) @@ -81,6 +81,25 @@ final class AuthTests: XCTestCase { wait(for: [responseExpectation], timeout: 5) } + func testUserRespondError() async { + let responseExpectation = expectation(description: "error response delivered") + let uri = try! await app.request(RequestParams.stub()) + try! await wallet.pair(uri: uri) + wallet.authRequestPublisher.sink { [unowned self] (id, message) in + Task(priority: .high) { + try! await wallet.reject(requestId: id) + } + } + .store(in: &publishers) + app.authResponsePublisher.sink { (id, result) in + guard case .failure(let error) = result else { XCTFail(); return } + XCTAssertEqual(error, .userRejeted) + responseExpectation.fulfill() + } + .store(in: &publishers) + wait(for: [responseExpectation], timeout: 5) + } + func testRespondSignatureVerificationFailed() async { let responseExpectation = expectation(description: "invalid signature response delivered") let uri = try! await app.request(RequestParams.stub()) @@ -89,7 +108,7 @@ final class AuthTests: XCTestCase { Task(priority: .high) { let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" let cacaoSignature = CacaoSignature(t: "eip191", s: invalidSignature) - try! await wallet.respond(requestId: id, result: .success(cacaoSignature)) + try! await wallet.respond(requestId: id, signature: cacaoSignature) } } .store(in: &publishers) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index ad4192861..158e54a2f 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -4,6 +4,7 @@ import WalletConnectUtils import WalletConnectPairing import WalletConnectRelay + public class AuthClient { enum Errors: Error { case malformedPairingURI @@ -87,9 +88,13 @@ public class AuthClient { try await appRequestService.request(params: params, topic: topic) } - public func respond(requestId: RPCID, result: Result) async throws { + public func respond(requestId: RPCID, signature: CacaoSignature) async throws { guard let account = account else { throw Errors.unknownWalletAddress } - try await walletRespondService.respond(requestId: requestId, result: result, account: account) + try await walletRespondService.respond(requestId: requestId, signature: signature, account: account) + } + + public func reject(requestId: RPCID) async throws { + try await walletRespondService.respondError(requestId: requestId) } public func getPendingRequests() throws -> [AuthRequest] { diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index bc7e13c72..a73015b4c 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -28,16 +28,23 @@ class AppRespondSubscriber { private func subscribeForResponse() { networkingInteractor.responsePublisher.sink { [unowned self] subscriptionPayload in + let response = subscriptionPayload.response guard - let requestId = subscriptionPayload.response.id, + let requestId = response.id, let request = rpcHistory.get(recordId: requestId)?.request, let requestParams = request.params, request.method == "wc_authRequest" else { return } networkingInteractor.unsubscribe(topic: subscriptionPayload.topic) + if let errorResponse = response.error, + let error = AuthError(code: errorResponse.code) { + onResponse?(requestId, .failure(error)) + return + } + guard - let cacao = try? subscriptionPayload.response.result?.get(Cacao.self), + let cacao = try? response.result?.get(Cacao.self), let address = try? DIDPKH(iss: cacao.payload.iss).account.address, let message = try? messageFormatter.formatMessage(from: cacao.payload) else { self.onResponse?(requestId, .failure(.malformedResponseParams)); return } diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index 0aba57f14..07e01871c 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -23,16 +23,7 @@ actor WalletRespondService { self.rpcHistory = rpcHistory } - func respond(requestId: RPCID, result: Result, account: Account) async throws { - switch result { - case .success(let signature): - try await respond(requestId: requestId, signature: signature, account: account) - case .failure: - try await respondError(requestId: requestId) - } - } - - private func respond(requestId: RPCID, signature: CacaoSignature, account: Account) async throws { + func respond(requestId: RPCID, signature: CacaoSignature, account: Account) async throws { let authRequestParams = try getAuthRequestParams(requestId: requestId) let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams) @@ -44,7 +35,7 @@ actor WalletRespondService { try await networkingInteractor.respond(topic: topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) } - private func respondError(requestId: RPCID) async throws { + func respondError(requestId: RPCID) async throws { let authRequestParams = try getAuthRequestParams(requestId: requestId) let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams) diff --git a/Sources/Auth/Types/Errors/AuthError.swift b/Sources/Auth/Types/Errors/AuthError.swift index 2b83d6182..f5b52b21a 100644 --- a/Sources/Auth/Types/Errors/AuthError.swift +++ b/Sources/Auth/Types/Errors/AuthError.swift @@ -10,6 +10,23 @@ public enum AuthError: Codable, Equatable, Error { extension AuthError: Reason { + init?(code: Int) { + switch code { + case Self.userRejeted.code: + self = .userRejeted + case Self.malformedResponseParams.code: + self = .malformedResponseParams + case Self.malformedRequestParams.code: + self = .malformedRequestParams + case Self.messageCompromised.code: + self = .messageCompromised + case Self.signatureVerificationFailed.code: + self = .signatureVerificationFailed + default: + return nil + } + } + public var code: Int { switch self { case .userRejeted: