Skip to content

Commit

Permalink
Auth Multiaccount Interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
flypaper0 committed Nov 30, 2022
1 parent 9d65bc5 commit b971eb2
Show file tree
Hide file tree
Showing 16 changed files with 103 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
"repositoryURL": "https://github.com/WalletConnect/Web3.swift",
"state": {
"branch": null,
"revision": "bdaaed96eee3a9bf7f341165f89a94288961d14c",
"revision": "a628f094f8088ec72a95792de18eeea24b997462",
"version": "1.0.0"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,5 @@ struct ThirdPartyConfigurator: Configurator {
url: "example.wallet",
icons: ["https://avatars.githubusercontent.com/u/37784886"]
))

Auth.configure(
account: Account("eip155:1:0xe5EeF1368781911d265fDB6946613dA61915a501")!
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@ import WalletConnectUtils

final class AuthRequestInteractor {

private let signer = MessageSignerFactory.create()
private let account = Account("eip155:1:0xe5EeF1368781911d265fDB6946613dA61915a501")!

func approve(request: AuthRequest) async throws {
let privateKey = Data(hex: "e56da0e170b5e09a8bb8f1b693392c7d56c3739a9c75740fbc558a2877868540")
let signer = MessageSignerFactory.create()
let signature = try signer.sign(message: request.message, privateKey: privateKey, type: .eip191)
try await Auth.instance.respond(requestId: request.id, signature: signature)
let signature = try signer.sign(
request: request,
address: account.address,
privateKey: privateKey,
type: .eip191)
try await Auth.instance.respond(requestId: request.id, signature: signature, from: account)
}

func reject(request: AuthRequest) async throws {
try await Auth.instance.reject(requestId: request.id)
}

func formatted(request: AuthRequest) -> String {
return try! signer.format(request: request, address: account.address)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ final class AuthRequestPresenter: ObservableObject {
}

var message: String {
return request.message
return interactor.formatted(request: request)
}

@MainActor
Expand Down
10 changes: 0 additions & 10 deletions Sources/Auth/Auth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,11 @@ public class Auth {
public static var instance: AuthClient = {
return AuthClientFactory.create(
metadata: Pair.metadata,
account: config?.account,
projectId: Networking.projectId,
networkingClient: Networking.interactor,
pairingRegisterer: Pair.registerer
)
}()

private static var config: Config?

private init() { }

/// Auth instance wallet config method
/// - Parameters:
/// - account: account that wallet will be authenticating with.
static public func configure(account: Account) {
Auth.config = Auth.Config(account: account)
}
}
12 changes: 2 additions & 10 deletions Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ import Combine
///
/// Access via `Auth.instance`
public class AuthClient {
enum Errors: Error {
case unknownWalletAddress
}

// MARK: - Public Properties

Expand Down Expand Up @@ -46,13 +43,11 @@ public class AuthClient {
private let walletRequestSubscriber: WalletRequestSubscriber
private let walletRespondService: WalletRespondService
private let pendingRequestsProvider: PendingRequestsProvider
private var account: Account?

init(appRequestService: AppRequestService,
appRespondSubscriber: AppRespondSubscriber,
walletRequestSubscriber: WalletRequestSubscriber,
walletRespondService: WalletRespondService,
account: Account?,
pendingRequestsProvider: PendingRequestsProvider,
logger: ConsoleLogging,
socketConnectionStatusPublisher: AnyPublisher<SocketConnectionStatus, Never>,
Expand All @@ -62,7 +57,6 @@ public class AuthClient {
self.walletRequestSubscriber = walletRequestSubscriber
self.walletRespondService = walletRespondService
self.appRespondSubscriber = appRespondSubscriber
self.account = account
self.pendingRequestsProvider = pendingRequestsProvider
self.logger = logger
self.socketConnectionStatusPublisher = socketConnectionStatusPublisher
Expand All @@ -83,8 +77,7 @@ public class AuthClient {
/// - Parameters:
/// - requestId: authentication request id
/// - signature: CACAO signature of requested message
public func respond(requestId: RPCID, signature: CacaoSignature) async throws {
guard let account = account else { throw Errors.unknownWalletAddress }
public func respond(requestId: RPCID, signature: CacaoSignature, from account: Account) async throws {
try await walletRespondService.respond(requestId: requestId, signature: signature, account: account)
}

Expand All @@ -96,8 +89,7 @@ public class AuthClient {

/// Query pending authentication requests
/// - Returns: Pending authentication requests
public func getPendingRequests() throws -> [AuthRequest] {
guard let account = account else { throw Errors.unknownWalletAddress }
public func getPendingRequests(account: Account) throws -> [AuthRequest] {
return try pendingRequestsProvider.getPendingRequests(account: account)
}

Expand Down
9 changes: 4 additions & 5 deletions Sources/Auth/AuthClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,29 @@ import Foundation

public struct AuthClientFactory {

public static func create(metadata: AppMetadata, account: Account?, projectId: String, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient {
public static func create(metadata: AppMetadata, projectId: String, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient {
let logger = ConsoleLogger(loggingLevel: .off)
let keyValueStorage = UserDefaults.standard
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
return AuthClientFactory.create(metadata: metadata, account: account, projectId: projectId, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient, pairingRegisterer: pairingRegisterer, iatProvider: DefaultIATProvider())
return AuthClientFactory.create(metadata: metadata, projectId: projectId, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient, pairingRegisterer: pairingRegisterer, iatProvider: DefaultIATProvider())
}

static func create(metadata: AppMetadata, account: Account?, projectId: String, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer, iatProvider: IATProvider) -> AuthClient {
static func create(metadata: AppMetadata, projectId: String, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer, iatProvider: IATProvider) -> AuthClient {
let kms = KeyManagementService(keychain: keychainStorage)
let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage)
let messageFormatter = SIWEMessageFormatter()
let appRequestService = AppRequestService(networkingInteractor: networkingClient, kms: kms, appMetadata: metadata, logger: logger, iatProvader: iatProvider)
let messageSigner = MessageSignerFactory.create(projectId: projectId)
let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingClient, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, pairingRegisterer: pairingRegisterer, messageFormatter: messageFormatter)
let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history)
let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingClient, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer)
let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingClient, logger: logger, kms: kms, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer)
let walletRespondService = WalletRespondService(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history, walletErrorResponder: walletErrorResponder)
let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history)

return AuthClient(appRequestService: appRequestService,
appRespondSubscriber: appRespondSubscriber,
walletRequestSubscriber: walletRequestSubscriber,
walletRespondService: walletRespondService,
account: account,
pendingRequestsProvider: pendingRequestsProvider,
logger: logger,
socketConnectionStatusPublisher: networkingClient.socketConnectionStatusPublisher,
Expand Down
7 changes: 0 additions & 7 deletions Sources/Auth/AuthConfig.swift

This file was deleted.

4 changes: 2 additions & 2 deletions Sources/Auth/Services/Common/SIWEMessageFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ protocol SIWEMessageFormatting {

struct SIWEMessageFormatter: SIWEMessageFormatting {
func formatMessage(from payload: AuthPayload, address: String) -> String? {
guard let chain = Blockchain(payload.chainId) else {return nil}
guard let chain = Blockchain(payload.chainId) else { return nil }
let message = SIWEMessage(domain: payload.domain,
uri: payload.aud,
address: address,
version: payload.version,
nonce: payload.nonce,
chainId: chain.reference,
chainId: chain.reference,
iat: payload.iat,
nbf: payload.nbf,
exp: payload.exp,
Expand Down
55 changes: 48 additions & 7 deletions Sources/Auth/Services/Signer/MessageSigner.swift
Original file line number Diff line number Diff line change
@@ -1,37 +1,78 @@
import Foundation

public protocol MessageSignatureVerifying {
func verify(signature: CacaoSignature, message: String, address: String, chainId: String) async throws
func verify(signature: CacaoSignature,
message: String,
address: String,
chainId: String
) async throws
}

public protocol MessageSigning {
func sign(message: String, privateKey: Data, type: CacaoSignatureType) throws -> CacaoSignature
func sign(request: AuthRequest,
address: String,
privateKey: Data,
type: CacaoSignatureType
) throws -> CacaoSignature
}

struct MessageSigner: MessageSignatureVerifying, MessageSigning {
public protocol MessageFormatting {
func format(request: AuthRequest, address: String) throws -> String
}

public typealias AuthMessageSigner = MessageSignatureVerifying & MessageSigning & MessageFormatting

struct MessageSigner: AuthMessageSigner {

enum Errors: Error {
case utf8EncodingFailed
case malformedRequestParams
}

private let signer: Signer
private let eip191Verifier: EIP191Verifier
private let eip1271Verifier: EIP1271Verifier
private let messageFormatter: SIWEMessageFormatting

init(signer: Signer, eip191Verifier: EIP191Verifier, eip1271Verifier: EIP1271Verifier) {
init(signer: Signer, eip191Verifier: EIP191Verifier, eip1271Verifier: EIP1271Verifier, messageFormatter: SIWEMessageFormatting) {
self.signer = signer
self.eip191Verifier = eip191Verifier
self.eip1271Verifier = eip1271Verifier
self.messageFormatter = messageFormatter
}

func format(request: AuthRequest, address: String) throws -> String {
guard let message = messageFormatter.formatMessage(
from: request.params.payloadParams,
address: address
) else { throw Errors.malformedRequestParams }

return message
}

func sign(message: String, privateKey: Data, type: CacaoSignatureType) throws -> CacaoSignature {
guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed }
func sign(request: AuthRequest,
address: String,
privateKey: Data,
type: CacaoSignatureType
) throws -> CacaoSignature {

let message = try format(request: request, address: address)

guard let messageData = message.data(using: .utf8)else {
throw Errors.utf8EncodingFailed
}

let signature = try signer.sign(message: prefixed(messageData), with: privateKey)
let prefixedHexSignature = "0x" + signature.toHexString()
return CacaoSignature(t: type, s: prefixedHexSignature)
}

func verify(signature: CacaoSignature, message: String, address: String, chainId: String) async throws {
func verify(signature: CacaoSignature,
message: String,
address: String,
chainId: String
) async throws {

guard let messageData = message.data(using: .utf8) else {
throw Errors.utf8EncodingFailed
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/Auth/Services/Signer/MessageSignerFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import Foundation

public struct MessageSignerFactory {

public static func create() -> MessageSigning & MessageSignatureVerifying {
public static func create() -> AuthMessageSigner {
return create(projectId: Networking.projectId)
}

static func create(projectId: String) -> MessageSigning & MessageSignatureVerifying {
static func create(projectId: String) -> AuthMessageSigner {
return MessageSigner(
signer: Signer(),
eip191Verifier: EIP191Verifier(),
eip1271Verifier: EIP1271Verifier(
projectId: projectId,
httpClient: HTTPNetworkClient(host: "rpc.walletconnect.com")
)
), messageFormatter: SIWEMessageFormatter()
)
}
}
5 changes: 2 additions & 3 deletions Sources/Auth/Services/Wallet/PendingRequestsProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ class PendingRequestsProvider {
let pendingRequests: [AuthRequest] = rpcHistory.getPending()
.filter {$0.request.method == "wc_authRequest"}
.compactMap {
guard let params = try? $0.request.params?.get(AuthRequestParams.self),
let message = SIWEMessageFormatter().formatMessage(from: params.payloadParams, address: account.address) else {return nil}
return AuthRequest(id: $0.request.id!, message: message)
guard let params = try? $0.request.params?.get(AuthRequestParams.self) else { return nil }
return AuthRequest(id: $0.request.id!, params: params)
}
return pendingRequests
}
Expand Down
28 changes: 8 additions & 20 deletions Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,32 @@ class WalletRequestSubscriber {
private let networkingInteractor: NetworkInteracting
private let logger: ConsoleLogging
private let kms: KeyManagementServiceProtocol
private let address: String?
private var publishers = [AnyCancellable]()
private let messageFormatter: SIWEMessageFormatting
private let walletErrorResponder: WalletErrorResponder
private let pairingRegisterer: PairingRegisterer
var onRequest: ((AuthRequest) -> Void)?

init(networkingInteractor: NetworkInteracting,
logger: ConsoleLogging,
kms: KeyManagementServiceProtocol,
messageFormatter: SIWEMessageFormatting,
address: String?,
walletErrorResponder: WalletErrorResponder,
pairingRegisterer: PairingRegisterer) {
init(
networkingInteractor: NetworkInteracting,
logger: ConsoleLogging,
kms: KeyManagementServiceProtocol,
walletErrorResponder: WalletErrorResponder,
pairingRegisterer: PairingRegisterer)
{
self.networkingInteractor = networkingInteractor
self.logger = logger
self.kms = kms
self.address = address
self.messageFormatter = messageFormatter
self.walletErrorResponder = walletErrorResponder
self.pairingRegisterer = pairingRegisterer
subscribeForRequest()
}

private func subscribeForRequest() {
guard let address = address else { return }

pairingRegisterer.register(method: AuthRequestProtocolMethod())
.sink { [unowned self] (payload: RequestSubscriptionPayload<AuthRequestParams>) in
logger.debug("WalletRequestSubscriber: Received request")
guard let message = messageFormatter.formatMessage(from: payload.request.payloadParams, address: address) else {
Task(priority: .high) {
try? await walletErrorResponder.respondError(AuthError.malformedRequestParams, requestId: payload.id)
}
return
}
pairingRegisterer.activate(pairingTopic: payload.topic)
onRequest?(.init(id: payload.id, message: message))
onRequest?(AuthRequest(id: payload.id, params: payload.request))
}.store(in: &publishers)
}
}
28 changes: 14 additions & 14 deletions Sources/Auth/Types/AuthPayload.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import Foundation

struct AuthPayload: Codable, Equatable {
let domain: String
let aud: String
let version: String
let nonce: String
let chainId: String
let type: String
let iat: String
let nbf: String?
let exp: String?
let statement: String?
let requestId: String?
let resources: [String]?
public struct AuthPayload: Codable, Equatable {
public let domain: String
public let aud: String
public let version: String
public let nonce: String
public let chainId: String
public let type: String
public let iat: String
public let nbf: String?
public let exp: String?
public let statement: String?
public let requestId: String?
public let resources: [String]?

init(requestParams: RequestParams, iat: String) {
public init(requestParams: RequestParams, iat: String) {
self.type = "eip4361"
self.chainId = requestParams.chainId
self.domain = requestParams.domain
Expand Down
Loading

0 comments on commit b971eb2

Please sign in to comment.