Skip to content

Commit

Permalink
Merge pull request #448 from WalletConnect/#416-auth-request-test
Browse files Browse the repository at this point in the history
[Auth] #416 auth request test
  • Loading branch information
llbartekll authored Aug 19, 2022
2 parents f10eec0 + 70d85d4 commit adf3328
Show file tree
Hide file tree
Showing 15 changed files with 111 additions and 25 deletions.
4 changes: 4 additions & 0 deletions Example/ExampleApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */; };
84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; };
84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; };
84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE684528ACDB4700C893FF /* RequestParams.swift */; };
A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50C036428AAD32200FE72D3 /* ClientDelegate.swift */; };
A50F3946288005B200064555 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50F3945288005B200064555 /* Types.swift */; };
A5629AA92876A23100094373 /* ChatService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AA82876A23100094373 /* ChatService.swift */; };
Expand Down Expand Up @@ -208,6 +209,7 @@
84D2A66528A4F51E0088AE09 /* AuthTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTests.swift; sourceTree = "<group>"; };
84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = "<group>"; };
84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = "<group>"; };
84FE684528ACDB4700C893FF /* RequestParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestParams.swift; sourceTree = "<group>"; };
A50C036428AAD32200FE72D3 /* ClientDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientDelegate.swift; sourceTree = "<group>"; };
A50F3945288005B200064555 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = "<group>"; };
A5629AA82876A23100094373 /* ChatService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatService.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -898,6 +900,7 @@
A5E03E1028646F8000888481 /* KeychainStorageMock.swift */,
A5E03E0E28646D8A00888481 /* WebSocketFactory.swift */,
A5E03DFC286465D100888481 /* Stubs.swift */,
84FE684528ACDB4700C893FF /* RequestParams.swift */,
);
path = Stubs;
sourceTree = "<group>";
Expand Down Expand Up @@ -1276,6 +1279,7 @@
767DC83528997F8E00080FA9 /* EthSendTransaction.swift in Sources */,
A5E03E03286466F400888481 /* ChatTests.swift in Sources */,
84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */,
84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */,
7694A5262874296A0001257E /* RegistryTests.swift in Sources */,
A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */,
A5E03E0D28646AD200888481 /* RelayClientEndToEndTests.swift in Sources */,
Expand Down
29 changes: 28 additions & 1 deletion Example/IntegrationTests/Auth/AuthTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import Combine
final class AuthTests: XCTestCase {
var app: AuthClient!
var wallet: AuthClient!
let prvKey = Data(hex: "462c1dad6832d7d96ccf87bd6a686a4110e114aaaebd5512e552c0e3a87b480f")
private var publishers = [AnyCancellable]()

override func setUp() {
app = makeClient(prefix: "👻 App")
let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")!
let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")!
wallet = makeClient(prefix: "🤑 Wallet", account: walletAccount)

let expectation = expectation(description: "Wait Clients Connected")
Expand Down Expand Up @@ -52,6 +53,32 @@ final class AuthTests: XCTestCase {
}

func testRequest() async {
let requestExpectation = expectation(description: "request delivered to wallet")
let uri = try! await app.request(RequestParams.stub())
try! await wallet.pair(uri: uri)
wallet.authRequestPublisher.sink { _ in
requestExpectation.fulfill()
}.store(in: &publishers)
wait(for: [requestExpectation], timeout: 2)
}

func testRespondSuccess() async {
let responseExpectation = expectation(description: "successful 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) {
let signature = try! MessageSigner(signer: Signer()).sign(message: message, privateKey: prvKey)
let cacaoSignature = CacaoSignature(t: "eip191", s: signature)
try! await wallet.respond(.success(RespondParams(id: id, signature: cacaoSignature)))
}
}
.store(in: &publishers)
app.authResponsePublisher.sink { (id, result) in
guard case .success = result else { XCTFail(); return }
responseExpectation.fulfill()
}
.store(in: &publishers)
wait(for: [responseExpectation], timeout: 2)
}
}
25 changes: 25 additions & 0 deletions Example/IntegrationTests/Stubs/RequestParams.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

import Foundation
@testable import Auth

extension RequestParams {
static func stub(domain: String = "service.invalid",
chainId: String = "1",
nonce: String = "32891756",
aud: String = "https://service.invalid/login",
nbf: String? = nil,
exp: String? = nil,
statement: String? = "I accept the ServiceOrg Terms of Service: https://service.invalid/tos",
requestId: String? = nil,
resources: [String]? = ["ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/", "https://example.com/my-web2-claim.json"]) -> RequestParams {
return RequestParams(domain: domain,
chainId: chainId,
nonce: nonce,
aud: aud,
nbf: nbf,
exp: exp,
statement: statement,
requestId: requestId,
resources: resources)
}
}
2 changes: 1 addition & 1 deletion Sources/Auth/AuthClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public struct AuthClientFactory {
let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history)
let messageFormatter = SIWEMessageFormatter()
let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore)
let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata)
let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata, logger: logger)
let messageSigner = MessageSigner(signer: Signer())
let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter)
let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore)
Expand Down
7 changes: 6 additions & 1 deletion Sources/Auth/Services/App/AppRequestService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ actor AppRequestService {
private let networkingInteractor: NetworkInteracting
private let appMetadata: AppMetadata
private let kms: KeyManagementService
private let logger: ConsoleLogging

init(networkingInteractor: NetworkInteracting,
kms: KeyManagementService,
appMetadata: AppMetadata) {
appMetadata: AppMetadata,
logger: ConsoleLogging) {
self.networkingInteractor = networkingInteractor
self.kms = kms
self.appMetadata = appMetadata
self.logger = logger
}

func request(params: RequestParams, topic: String) async throws {
Expand All @@ -24,6 +27,8 @@ actor AppRequestService {
let payload = AuthPayload(requestParams: params, iat: issueAt)
let params = AuthRequestParams(requester: requester, payloadParams: payload)
let request = RPCRequest(method: "wc_authRequest", params: params)
try kms.setPublicKey(publicKey: pubKey, for: responseTopic)
logger.debug("AppRequestService: Subscribibg for response topic: \(responseTopic)")
try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: AuthRequestParams.tag)
try await networkingInteractor.subscribe(topic: responseTopic)
}
Expand Down
1 change: 1 addition & 0 deletions Sources/Auth/Services/App/AppRespondSubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class AppRespondSubscriber {
message: message,
address: address
)
logger.debug("Received response with valid signature")
onResponse?(requestId, .success(cacao))
} catch {
logger.debug("Received response with invalid signature")
Expand Down
8 changes: 5 additions & 3 deletions Sources/Auth/Services/Common/CacaoFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import Foundation
import WalletConnectUtils

protocol CacaoFormatting {
func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ account: Account) -> Cacao
func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ didpkh: DIDPKH) -> Cacao
}

class CacaoFormatter: CacaoFormatting {
func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ account: Account) -> Cacao {
fatalError("not implemented")
func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ didpkh: DIDPKH) -> Cacao {
let header = CacaoHeader(t: "eip4361")
let payload = CacaoPayload(params: request.payloadParams, didpkh: didpkh)
return Cacao(header: header, payload: payload, signature: signature)
}
}
8 changes: 7 additions & 1 deletion Sources/Auth/Services/Common/NetworkingInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,13 @@ class NetworkingInteractor: NetworkInteracting {
}

func unsubscribe(topic: String) {
fatalError("not implemented")
relayClient.unsubscribe(topic: topic) { [unowned self] error in
if let error = error {
logger.error(error)
} else {
rpcHistory.deleteAll(forTopic: topic)
}
}
}

func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws {
Expand Down
10 changes: 5 additions & 5 deletions Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ class WalletRequestSubscriber {
self.logger = logger
self.address = address
self.messageFormatter = messageFormatter
subscribeForRequest()
if address != nil {
subscribeForRequest()
}
}

private func subscribeForRequest() {
guard let address = address else {
logger.warn("unexpected request")
return
}
guard let address = address else {return}
networkingInteractor.requestPublisher.sink { [unowned self] subscriptionPayload in
logger.debug("WalletRequestSubscriber: Received request")
guard
let requestId = subscriptionPayload.request.id,
subscriptionPayload.request.method == "wc_authRequest" else { return }
Expand Down
11 changes: 6 additions & 5 deletions Sources/Auth/Services/Wallet/WalletRespondService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,19 @@ actor WalletRespondService {
}

private func respond(respondParams: RespondParams, account: Account) async throws {
guard let request = rpcHistory.get(recordId: RPCID(respondParams.id))?.request else { throw Errors.recordForIdNotFound }
guard let request = rpcHistory.get(recordId: respondParams.id)?.request else { throw Errors.recordForIdNotFound }
guard let authRequestParams = try? request.params?.get(AuthRequestParams.self) else { throw Errors.malformedAuthRequestParams }

let peerPubKey = authRequestParams.requester.publicKey
let peerPubKey = try AgreementPublicKey(hex: authRequestParams.requester.publicKey)
let responseTopic = peerPubKey.rawRepresentation.sha256().toHexString()
let selfPubKey = try kms.createX25519KeyPair()
let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey)
let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey.hexRepresentation)
try kms.setAgreementSecret(agreementKeys, topic: responseTopic)

let cacao = CacaoFormatter().format(authRequestParams, respondParams.signature, account)
let didpkh = DIDPKH(account: account)
let cacao = CacaoFormatter().format(authRequestParams, respondParams.signature, didpkh)
let response = RPCResponse(id: request.id!, result: cacao)

try await networkingInteractor.respond(topic: respondParams.topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: selfPubKey.rawRepresentation))
try await networkingInteractor.respond(topic: responseTopic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: selfPubKey.rawRepresentation))
}
}
24 changes: 19 additions & 5 deletions Sources/Auth/Types/Cacao/CacaoPayload.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,23 @@ struct CacaoPayload: Codable, Equatable {
let version: Int
let nonce: String
let iat: String
let nbf: String
let exp: String
let statement: String
let requestId: String
let resources: [String]
let nbf: String?
let exp: String?
let statement: String?
let requestId: String?
let resources: [String]?

init(params: AuthPayload, didpkh: DIDPKH) {
self.iss = didpkh.iss
self.domain = params.domain
self.aud = params.aud
self.version = 1
self.nonce = params.nonce
self.iat = params.iat
self.nbf = params.nbf
self.exp = params.exp
self.statement = params.statement
self.requestId = params.requestId
self.resources = params.resources
}
}
2 changes: 1 addition & 1 deletion Sources/Auth/Types/Cacao/CacaoSignature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import Foundation
struct CacaoSignature: Codable, Equatable {
let t: String
let s: String
let m: String
let m: String? = nil
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Foundation
import WalletConnectUtils

/// wc_authRequest RPC method request param
struct AuthRequestParams: Codable, Equatable {
let requester: Requester
let payloadParams: AuthPayload
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Foundation
import WalletConnectUtils

/// wc_authRequest RPC method respond param
struct AuthResponseParams: Codable, Equatable {
let header: CacaoHeader
let payload: CacaoPayload
Expand Down
3 changes: 1 addition & 2 deletions Sources/Auth/Types/RespondParams.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Foundation

public struct RespondParams {
let id: Int64
let topic: String
let id: RPCID
let signature: CacaoSignature
}

0 comments on commit adf3328

Please sign in to comment.