Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Auth] #378 auth client #425

Merged
merged 10 commits into from
Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 98 additions & 8 deletions Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
@@ -1,31 +1,121 @@
import Foundation
import Combine
import JSONRPC
import WalletConnectUtils
import WalletConnectPairing

class AuthClient {
enum Errors: Error {
case malformedPairingURI
case unknownWalletAddress
case noPairingMatchingTopic
}
private var authRequestPublisherSubject = PassthroughSubject<(id: RPCID, message: String), Never>()
public var authRequestPublisher: AnyPublisher<(id: RPCID, message: String), Never> {
authRequestPublisherSubject.eraseToAnyPublisher()
}

private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, cacao: Cacao), Never>()
public var authResponsePublisher: AnyPublisher<(id: RPCID, cacao: Cacao), Never> {
authResponsePublisherSubject.eraseToAnyPublisher()
}
private let rpcHistory: RPCHistory

private let appPairService: AppPairService
private let appRequestService: AuthRequestService
private let appRequestService: AppRequestService
private let appRespondSubscriber: AppRespondSubscriber

private let walletPairService: WalletPairService
private let walletRequestSubscriber: WalletRequestSubscriber
private let walletRespondService: WalletRespondService
private let cleanupService: CleanupService
private let pairingStorage: WCPairingStorage
public let logger: ConsoleLogging

init(appPairService: AppPairService, appRequestService: AuthRequestService, walletPairService: WalletPairService) {
private var account: Account?

init(appPairService: AppPairService,
appRequestService: AppRequestService,
appRespondSubscriber: AppRespondSubscriber,
walletPairService: WalletPairService,
walletRequestSubscriber: WalletRequestSubscriber,
walletRespondService: WalletRespondService,
account: Account?,
rpcHistory: RPCHistory,
cleanupService: CleanupService,
logger: ConsoleLogging,
pairingStorage: WCPairingStorage) {
self.appPairService = appPairService
self.appRequestService = appRequestService
self.walletPairService = walletPairService
}
self.walletRequestSubscriber = walletRequestSubscriber
self.walletRespondService = walletRespondService
self.appRespondSubscriber = appRespondSubscriber
self.account = account
self.rpcHistory = rpcHistory
self.cleanupService = cleanupService
self.logger = logger
self.pairingStorage = pairingStorage

func request(params: RequestParams) async throws -> String {
let uri = try await appPairService.create()
try await appRequestService.request(params: params, topic: uri.topic)
return uri.absoluteString
setUpPublishers()
}

func pair(uri: String) async throws {
public func pair(uri: String) async throws {
guard let pairingURI = WalletConnectURI(string: uri) else {
throw Errors.malformedPairingURI
}
try await walletPairService.pair(pairingURI)
}

public func request(_ params: RequestParams, topic: String?) async throws -> String? {
llbartekll marked this conversation as resolved.
Show resolved Hide resolved
logger.debug("Requesting Authentication")
if let topic = topic {
guard pairingStorage.hasPairing(forTopic: topic) else {
throw Errors.noPairingMatchingTopic
}
logger.debug("Requesting on existing pairing")
try await appRequestService.request(params: params, topic: topic)
return nil
} else {
let uri = try await appPairService.create()
try await appRequestService.request(params: params, topic: uri.topic)
return uri.absoluteString
}
}

public func respond(_ params: RespondParams) async throws {
guard let account = account else { throw Errors.unknownWalletAddress }
try await walletRespondService.respond(respondParams: params, account: account)
}

public func getPendingRequests() throws -> [AuthRequest] {
guard let account = account else { throw Errors.unknownWalletAddress }
let pendingRequests: [AuthRequest] = rpcHistory.getPending()
.filter {$0.request.method == "wc_authRequest"}
llbartekll marked this conversation as resolved.
Show resolved Hide resolved
.compactMap {
guard let params = try? $0.request.params?.get(AuthRequestParams.self) else {return nil}
let message = SIWEMessageFormatter().formatMessage(from: params.payloadParams, address: account.address)
return AuthRequest(id: $0.request.id!, message: message)
}
return pendingRequests
}

#if DEBUG
/// Delete all stored data sach as: pairings, sessions, keys
///
/// - Note: Doesn't unsubscribe from topics
public func cleanup() throws {
try cleanupService.cleanup()
}
#endif

private func setUpPublishers() {
appRespondSubscriber.onResponse = { [unowned self] (id, cacao) in
authResponsePublisherSubject.send((id, cacao))
}

walletRequestSubscriber.onRequest = { [unowned self] (id, message) in
authRequestPublisherSubject.send((id, message))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import WalletConnectUtils
import WalletConnectKMS
import JSONRPC

actor AuthRequestService {
actor AppRequestService {
private let networkingInteractor: NetworkInteracting
private let appMetadata: AppMetadata
private let kms: KeyManagementService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation
import WalletConnectUtils
import JSONRPC

class AuthRespondSubscriber {
class AppRespondSubscriber {
private let networkingInteractor: NetworkInteracting
private let logger: ConsoleLogging
private let rpcHistory: RPCHistory
Expand Down
4 changes: 2 additions & 2 deletions Sources/Auth/Services/Common/CacaoFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import Foundation
import WalletConnectUtils

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

class CacaoFormatter: CacaoFormatting {
func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ issuer: Account) -> Cacao {
func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ account: Account) -> Cacao {
fatalError("not implemented")
}
}
20 changes: 20 additions & 0 deletions Sources/Auth/Services/Common/CleanupService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Foundation
import WalletConnectKMS
import WalletConnectUtils
import WalletConnectPairing

final class CleanupService {

private let pairingStore: WCPairingStorage
private let kms: KeyManagementServiceProtocol

init(pairingStore: WCPairingStorage, kms: KeyManagementServiceProtocol, sessionToPairingTopic: CodableStore<String>) {
self.pairingStore = pairingStore
self.kms = kms
}

func cleanup() throws {
pairingStore.deleteAll()
try kms.deleteAll()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation
import WalletConnectUtils
import JSONRPC

class AuthRequestSubscriber {
class WalletRequestSubscriber {
private let networkingInteractor: NetworkInteracting
private let logger: ConsoleLogging
private let address: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import WalletConnectKMS
import JSONRPC
import WalletConnectUtils

actor AuthRespondService {
actor WalletRespondService {
enum Errors: Error {
case recordForIdNotFound
case malformedAuthRequestParams
Expand All @@ -23,7 +23,7 @@ actor AuthRespondService {
self.rpcHistory = rpcHistory
}

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

Expand All @@ -33,7 +33,7 @@ actor AuthRespondService {
let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey)
try kms.setAgreementSecret(agreementKeys, topic: responseTopic)

let cacao = CacaoFormatter().format(authRequestParams, respondParams.signature, issuer)
let cacao = CacaoFormatter().format(authRequestParams, respondParams.signature, account)
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))
Expand Down
4 changes: 4 additions & 0 deletions Sources/Auth/Types/Aliases/Account.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import WalletConnectUtils
import Foundation

typealias Account = WalletConnectUtils.Account
4 changes: 4 additions & 0 deletions Sources/Auth/Types/Aliases/RPCID.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Foundation
import JSONRPC

public typealias RPCID = JSONRPC.RPCID
7 changes: 7 additions & 0 deletions Sources/Auth/Types/Public/AuthRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

public struct AuthRequest: Equatable, Codable {
public let id: RPCID
/// EIP-4361: Sign-In with Ethereum message
public let message: String
}
3 changes: 0 additions & 3 deletions Sources/WalletConnectSign/Sign/SignClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import WalletConnectRelay
import WalletConnectUtils
import WalletConnectKMS
import Combine
#if os(iOS)
import UIKit
#endif

/// An Object that expose public API to provide interactions with WalletConnect SDK
///
Expand Down
4 changes: 4 additions & 0 deletions Sources/WalletConnectUtils/RPCHistory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,8 @@ public final class RPCHistory {
}
}
}

public func getPending() -> [Record] {
storage.getAll().filter {$0.response == nil}
}
}
4 changes: 2 additions & 2 deletions Tests/AuthTests/AuthRequstSubscriberTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import JSONRPC

class AuthRequstSubscriberTests: XCTestCase {
var networkingInteractor: NetworkingInteractorMock!
var sut: AuthRequestSubscriber!
var sut: WalletRequestSubscriber!
var messageFormatter: SIWEMessageFormatterMock!
let defaultTimeout: TimeInterval = 0.01

override func setUp() {
networkingInteractor = NetworkingInteractorMock()
messageFormatter = SIWEMessageFormatterMock()
sut = AuthRequestSubscriber(networkingInteractor: networkingInteractor,
sut = WalletRequestSubscriber(networkingInteractor: networkingInteractor,
logger: ConsoleLoggerMock(),
messageFormatter: messageFormatter, address: "")
}
Expand Down