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 all 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
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
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