Skip to content

Commit

Permalink
Merge pull request #425 from WalletConnect/#378-auth-client
Browse files Browse the repository at this point in the history
[Auth] #378 auth client
  • Loading branch information
llbartekll authored Aug 11, 2022
2 parents d1af393 + 0913eff commit 880cc73
Show file tree
Hide file tree
Showing 16 changed files with 163 additions and 27 deletions.
96 changes: 89 additions & 7 deletions Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
@@ -1,31 +1,113 @@
import Foundation
import Combine
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 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
private let pendingRequestsProvider: PendingRequestsProvider
public let logger: ConsoleLogging

private var account: Account?

init(appPairService: AppPairService, appRequestService: AuthRequestService, walletPairService: WalletPairService) {
init(appPairService: AppPairService,
appRequestService: AppRequestService,
appRespondSubscriber: AppRespondSubscriber,
walletPairService: WalletPairService,
walletRequestSubscriber: WalletRequestSubscriber,
walletRespondService: WalletRespondService,
account: Account?,
pendingRequestsProvider: PendingRequestsProvider,
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.pendingRequestsProvider = pendingRequestsProvider
self.cleanupService = cleanupService
self.logger = logger
self.pairingStorage = pairingStorage

setUpPublishers()
}

func request(params: RequestParams) async throws -> String {
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) async throws -> String {
logger.debug("Requesting Authentication")
let uri = try await appPairService.create()
try await appRequestService.request(params: params, topic: uri.topic)
return uri.absoluteString
}

func pair(uri: String) async throws {
guard let pairingURI = WalletConnectURI(string: uri) else {
throw Errors.malformedPairingURI
public func request(_ params: RequestParams, topic: String) async throws {
logger.debug("Requesting Authentication on existing pairing")
guard pairingStorage.hasPairing(forTopic: topic) else {
throw Errors.noPairingMatchingTopic
}
try await appRequestService.request(params: params, topic: topic)
}

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 }
return try pendingRequestsProvider.getPendingRequests(account: account)
}

#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))
}
try await walletPairService.pair(pairingURI)
}
}

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()
}
}
22 changes: 22 additions & 0 deletions Sources/Auth/Services/Wallet/PendingRequestsProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Foundation
import JSONRPC
import WalletConnectUtils

class PendingRequestsProvider {
private let rpcHistory: RPCHistory

init(rpcHistory: RPCHistory) {
self.rpcHistory = rpcHistory
}

public func getPendingRequests(account: Account) throws -> [AuthRequest] {
let pendingRequests: [AuthRequest] = rpcHistory.getPending()
.filter {$0.request.method == "wc_authRequest"}
.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
}
}
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 All @@ -29,13 +29,9 @@ class AuthRequestSubscriber {
logger.debug("Malformed auth request params")
return
}
do {
let message = try messageFormatter.formatMessage(from: authRequestParams.payloadParams, address: address)
guard let requestId = subscriptionPayload.request.id else { return }
onRequest?(requestId, message)
} catch {
logger.debug(error)
}
let message = messageFormatter.formatMessage(from: authRequestParams.payloadParams, address: address)
guard let requestId = subscriptionPayload.request.id else { return }
onRequest?(requestId, message)
}.store(in: &publishers)
}

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
File renamed without changes.
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
File renamed without changes.
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

0 comments on commit 880cc73

Please sign in to comment.