diff --git a/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.pbxproj b/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.pbxproj index 841f368..f6d5904 100644 --- a/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.pbxproj +++ b/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.pbxproj @@ -52,6 +52,7 @@ 511CEAEF2452D4EF00A7ACE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51721D842474E4B700C22BAD /* torus-direct-swift-sdk */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "torus-direct-swift-sdk"; path = ..; sourceTree = ""; }; 517C5F5D24693F9D006D5A43 /* CustomAuthDemo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CustomAuthDemo.entitlements; sourceTree = ""; }; + A3C24F222A70FAF9002F4FC9 /* customauth-swift-sdk */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "customauth-swift-sdk"; path = ..; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -91,6 +92,7 @@ 511CEABF2452D4EC00A7ACE9 = { isa = PBXGroup; children = ( + A3C24F212A70FAF9002F4FC9 /* Packages */, 511CEACA2452D4EC00A7ACE9 /* CustomAuthDemo */, 511CEAE12452D4EF00A7ACE9 /* CustomAuthDemoTests */, 511CEAEC2452D4EF00A7ACE9 /* CustomAuthDemoUITests */, @@ -151,6 +153,14 @@ path = CustomAuthDemoUITests; sourceTree = ""; }; + A3C24F212A70FAF9002F4FC9 /* Packages */ = { + isa = PBXGroup; + children = ( + A3C24F222A70FAF9002F4FC9 /* customauth-swift-sdk */, + ); + name = Packages; + sourceTree = ""; + }; A84257F51AACA6652F347CFD /* Pods */ = { isa = PBXGroup; children = ( diff --git a/Package.resolved b/Package.resolved index f1cd1db..b858466 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,6 +1,15 @@ { "object": { "pins": [ + { + "package": "AnyCodable", + "repositoryURL": "https://github.com/Flight-School/AnyCodable", + "state": { + "branch": null, + "revision": "862808b2070cd908cb04f9aafe7de83d35f81b05", + "version": "0.6.7" + } + }, { "package": "BigInt", "repositoryURL": "https://github.com/attaswift/BigInt", @@ -24,17 +33,8 @@ "repositoryURL": "https://github.com/torusresearch/fetch-node-details-swift.git", "state": { "branch": null, - "revision": "94a0ee598abfcc2104ceeda80eecf4b55242ba62", - "version": "4.0.1" - } - }, - { - "package": "GenericJSON", - "repositoryURL": "https://github.com/zoul/generic-json-swift", - "state": { - "branch": null, - "revision": "0a06575f4038b504e78ac330913d920f1630f510", - "version": "2.0.2" + "revision": "68459eb481c382bfbbe28345b8eb25b3897c7447", + "version": "5.0.0" } }, { @@ -57,11 +57,11 @@ }, { "package": "secp256k1", - "repositoryURL": "https://github.com/Boilertalk/secp256k1.swift", + "repositoryURL": "https://github.com/GigaBitcoin/secp256k1.swift.git", "state": { "branch": null, - "revision": "cd187c632fb812fd93711a9f7e644adb7e5f97f0", - "version": "0.1.7" + "revision": "1a14e189def5eaa92f839afdd2faad8e43b61a6e", + "version": "0.12.2" } }, { @@ -78,17 +78,8 @@ "repositoryURL": "https://github.com/torusresearch/torus-utils-swift.git", "state": { "branch": null, - "revision": "5545ce7e4d00e1a5821434c4b673258235dd05d5", - "version": "5.1.1" - } - }, - { - "package": "web3.swift", - "repositoryURL": "https://github.com/argentlabs/web3.swift", - "state": { - "branch": null, - "revision": "0474eb5e883bc4800b3909833207a1d35e72b808", - "version": "0.9.3" + "revision": "61b36686c2def5f2f40d8cb0d5bc3af34559c1d5", + "version": "6.0.1" } } ] diff --git a/Package.swift b/Package.swift index afd27ee..621481c 100644 --- a/Package.swift +++ b/Package.swift @@ -14,7 +14,7 @@ let package = Package( targets: ["CustomAuth"]) ], dependencies: [ - .package(name: "TorusUtils", url: "https://github.com/torusresearch/torus-utils-swift.git", from: "5.1.1"), + .package(name: "TorusUtils", url: "https://github.com/torusresearch/torus-utils-swift.git", from: "6.0.1"), .package(name: "jwt-kit", url: "https://github.com/vapor/jwt-kit.git", from: "4.13.0"), .package(name: "JWTDecode", url: "https://github.com/auth0/JWTDecode.swift.git", from: "3.1.0") ], diff --git a/Sources/CustomAuth/CustomAuth.swift b/Sources/CustomAuth/CustomAuth.swift index eb2ec33..696a8a9 100644 --- a/Sources/CustomAuth/CustomAuth.swift +++ b/Sources/CustomAuth/CustomAuth.swift @@ -10,15 +10,20 @@ import Foundation import OSLog import TorusUtils import UIKit +import CommonSources // Global variable var tsSdkLogType = OSLogType.default +public struct TorusKeyData { + public let torusKey : TorusKey + public var userInfo : [String: Any] +} + /// Provides integration of an iOS app with Torus CustomAuth. open class CustomAuth { - let factory: CASDKFactoryProtocol - public var torusUtils: AbstractTorusUtils - let fetchNodeDetails: FetchNodeDetails + var nodeDetailManager: NodeDetailManager + var torusUtils: AbstractTorusUtils var urlSession: URLSession var enableOneKey: Bool /// You can pass your own custom url rather than using our default infura url, @@ -29,6 +34,7 @@ open class CustomAuth { public let subVerifierDetails: [SubVerifierDetails] public var authorizeURLHandler: URLOpenerTypes? var observer: NSObjectProtocol? // useful for Notifications + var network : TorusNetwork /// Initiate an CustomAuth instance. /// - Parameters: @@ -38,20 +44,21 @@ open class CustomAuth { /// - factory: Providng mocking by implementing TDSDKFactoryProtocol. /// - network: Etherum network to be used. /// - loglevel: Indicates the log level of this instance. All logs lower than this level will be ignored. - public init(aggregateVerifierType: verifierTypes, aggregateVerifier: String, subVerifierDetails: [SubVerifierDetails], factory: CASDKFactoryProtocol = CASDKFactory(), network: EthereumNetworkFND = .MAINNET, loglevel: OSLogType = .debug, urlSession: URLSession = URLSession.shared, enableOneKey: Bool = false, networkUrl: String? = nil) { + public init(aggregateVerifierType: verifierTypes, aggregateVerifier: String, subVerifierDetails: [SubVerifierDetails], network: TorusNetwork = .legacy(.MAINNET), loglevel: OSLogType = .debug, urlSession: URLSession = URLSession.shared, enableOneKey: Bool = false, networkUrl: String? = nil) { tsSdkLogType = loglevel self.networkUrl = networkUrl self.enableOneKey = enableOneKey // factory method - self.factory = factory + self.nodeDetailManager = NodeDetailManager(network: network) self.urlSession = urlSession - torusUtils = factory.createTorusUtils(loglevel: loglevel, urlSession: urlSession, enableOneKey: enableOneKey, network: network) - fetchNodeDetails = factory.createFetchNodeDetails(network: network, urlSession: urlSession, networkUrl: networkUrl) + + self.torusUtils = TorusUtils(loglevel: loglevel, urlSession: urlSession, enableOneKey: enableOneKey, serverTimeOffset: 1000, network: network) // verifier details self.aggregateVerifier = aggregateVerifier self.aggregateVerifierType = aggregateVerifierType self.subVerifierDetails = subVerifierDetails + self.network = network } /// Initiate an CustomAuth instance. @@ -60,19 +67,19 @@ open class CustomAuth { /// - aggregateVerifier: Name of the verifier to be used.. /// - subVerifierDetails: Details of each subverifiers to be used. public convenience init(aggregateVerifierType: verifierTypes, aggregateVerifier: String, subVerifierDetails: [SubVerifierDetails], enableOneKey: Bool = false, networkUrl: String? = nil) { - let factory = CASDKFactory() - self.init(aggregateVerifierType: aggregateVerifierType, aggregateVerifier: aggregateVerifier, subVerifierDetails: subVerifierDetails, factory: factory, network: .MAINNET, loglevel: .debug, enableOneKey: enableOneKey, networkUrl: networkUrl) +// let factory = CASDKFactory() + self.init(aggregateVerifierType: aggregateVerifierType, aggregateVerifier: aggregateVerifier, subVerifierDetails: subVerifierDetails, network: .legacy(.MAINNET), loglevel: .debug, enableOneKey: enableOneKey, networkUrl: networkUrl) } public convenience init(aggregateVerifierType: verifierTypes, aggregateVerifier: String, subVerifierDetails: [SubVerifierDetails], loglevel: OSLogType = .debug, enableOneKey: Bool = false, networkUrl: String? = nil) { - let factory = CASDKFactory() - self.init(aggregateVerifierType: aggregateVerifierType, aggregateVerifier: aggregateVerifier, subVerifierDetails: subVerifierDetails, factory: factory, network: .MAINNET, loglevel: loglevel, enableOneKey: enableOneKey, networkUrl: networkUrl) +// let factory = CASDKFactory() + self.init(aggregateVerifierType: aggregateVerifierType, aggregateVerifier: aggregateVerifier, subVerifierDetails: subVerifierDetails, network: .legacy(.MAINNET), loglevel: loglevel, enableOneKey: enableOneKey, networkUrl: networkUrl) } /// Retrieve information of Torus nodes from a predefined Etherum contract. /// - Returns: An array of URLs to the nodes. open func getNodeDetailsFromContract(verifier: String, verfierID: String) async throws -> AllNodeDetailsModel { - let nodeDetails = try await fetchNodeDetails.getNodeDetails(verifier: verifier, verifierID: verfierID) + let nodeDetails = try await nodeDetailManager.getNodeDetails(verifier: verifier, verifierID: verfierID) return nodeDetails } @@ -82,7 +89,7 @@ open class CustomAuth { /// - browserType: Indicates the way to open the browser for login flow. Use `.external` for opening system safari, or `.asWebAuthSession` for opening an in-app ASwebAuthenticationSession. /// - modalPresentationStyle: Indicates the UIModalPresentationStyle for the popup. /// - Returns: A promise that resolve with a Dictionary that contain at least `privateKey` and `publicAddress` field.. - open func triggerLogin(controller: UIViewController? = nil, browserType: URLOpenerTypes = .asWebAuthSession, modalPresentationStyle: UIModalPresentationStyle = .fullScreen) async throws -> [String: Any] { + open func triggerLogin(controller: UIViewController? = nil, browserType: URLOpenerTypes = .asWebAuthSession, modalPresentationStyle: UIModalPresentationStyle = .fullScreen) async throws -> TorusKeyData { os_log("triggerLogin called with %@ %@", log: getTorusLogger(log: CASDKLogger.core, type: .info), type: .info, browserType.rawValue, modalPresentationStyle.rawValue) // Set browser authorizeURLHandler = browserType @@ -101,16 +108,25 @@ open class CustomAuth { } } - open func handleSingleLogins(controller: UIViewController?, modalPresentationStyle: UIModalPresentationStyle = .fullScreen) async throws -> [String: Any] { + open func handleSingleLogins(controller: UIViewController?, modalPresentationStyle: UIModalPresentationStyle = .fullScreen) async throws -> TorusKeyData { if let subVerifier = subVerifierDetails.first { let loginURL = subVerifier.getLoginURL() await openURL(url: loginURL, view: controller, modalPresentationStyle: modalPresentationStyle) - let url = await withUnsafeContinuation({ continuation in - observeCallback { url in + + let url = try await withUnsafeThrowingContinuation { (continuation: UnsafeContinuation) in + observeCallbackWithError { url, err in + guard + err == nil, + let url = url + else { + continuation.resume(throwing: err!) + return + } + continuation.resume(returning: url) + return + } } - }) - let responseParameters = self.parseURL(url: url) os_log("ResponseParams after redirect: %@", log: getTorusLogger(log: CASDKLogger.core, type: .info), type: .info, responseParameters) do { @@ -121,8 +137,13 @@ open class CustomAuth { let idToken = data["tokenForKeys"] as! String data.removeValue(forKey: "tokenForKeys") data.removeValue(forKey: "verifierId") + + let torusKey = try await getTorusKey(verifier: self.aggregateVerifier, verifierId: verifierId, idToken: idToken, userData: data) - return torusKey + var mergedUserInfo = newData.merging(responseParameters) { (_, new) in new } + mergedUserInfo["verifier"] = self.aggregateVerifier + let result = TorusKeyData(torusKey: torusKey, userInfo: mergedUserInfo) + return result } catch { os_log("handleSingleLogin: err: %s", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error, error.localizedDescription) throw error @@ -133,18 +154,27 @@ open class CustomAuth { throw CASDKError.unknownError } - open func handleSingleIdVerifier(controller: UIViewController?, modalPresentationStyle: UIModalPresentationStyle = .fullScreen) async throws -> [String: Any] { + open func handleSingleIdVerifier(controller: UIViewController?, modalPresentationStyle: UIModalPresentationStyle = .fullScreen) async throws -> TorusKeyData { if let subVerifier = subVerifierDetails.first { let loginURL = subVerifier.getLoginURL() await MainActor.run(body: { openURL(url: loginURL, view: controller, modalPresentationStyle: modalPresentationStyle) }) - let url = await withUnsafeContinuation({ continuation in - observeCallback { url in + let url = try await withUnsafeThrowingContinuation { (continuation: UnsafeContinuation) in + observeCallbackWithError { url, err in + guard + err == nil, + let url = url + else { + continuation.resume(throwing: err!) + return + } + continuation.resume(returning: url) + return + } } - }) let responseParameters = self.parseURL(url: url) os_log("ResponseParams after redirect: %@", log: getTorusLogger(log: CASDKLogger.core, type: .info), type: .info, responseParameters) do { @@ -155,7 +185,10 @@ open class CustomAuth { data.removeValue(forKey: "tokenForKeys") data.removeValue(forKey: "verifierId") let aggTorusKey = try await getAggregateTorusKey(verifier: self.aggregateVerifier, verifierId: verifierId, idToken: idToken, subVerifierDetails: subVerifier, userData: newData) - return aggTorusKey + var mergedUserInfo = newData.merging(responseParameters) { (_, new) in new } + mergedUserInfo["verifier"] = self.aggregateVerifier + let result = TorusKeyData(torusKey: aggTorusKey, userInfo: mergedUserInfo) + return result } catch { os_log("handleSingleIdVerifier err: %s", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error, error.localizedDescription) throw error @@ -166,12 +199,12 @@ open class CustomAuth { } - func handleAndAggregateVerifier(controller: UIViewController?) async throws -> [String: Any] { + func handleAndAggregateVerifier(controller: UIViewController?) async throws -> TorusKeyData { // TODO: implement verifier throw CASDKError.methodUnavailable } - func handleOrAggregateVerifier(controller: UIViewController?) async throws -> [String: Any] { + func handleOrAggregateVerifier(controller: UIViewController?) async throws -> TorusKeyData { // TODO: implement verifier throw CASDKError.methodUnavailable } @@ -183,18 +216,16 @@ open class CustomAuth { /// - idToken: Access token received from the OAuth provider. /// - userData: Custom data that will be returned with `privateKey` and `publicAddress`. /// - Returns: A promise that resolve with a Dictionary that contain at least `privateKey` and `publicAddress` field.. - open func getTorusKey(verifier: String, verifierId: String, idToken: String, userData: [String: Any] = [:]) async throws -> [String: Any] { - let extraParams = ["verifier_id": verifierId] as [String: Any] + open func getTorusKey(verifier: String, verifierId: String, idToken: String, userData: [String: Any] = [:]) async throws -> TorusKey { + let extraParams = ["verifier_id": verifierId] as [String: Codable] let buffer: Data = try! NSKeyedArchiver.archivedData(withRootObject: extraParams, requiringSecureCoding: false) + let verifierParams = VerifierParams(verifier_id: verifierId) do { - let nodeDetails = try await getNodeDetailsFromContract(verifier: verifier, verfierID: verifierId) - let responseFromRetrieveShares = try await torusUtils.retrieveShares(torusNodePubs: nodeDetails.getTorusNodePub(), endpoints: nodeDetails.getTorusNodeEndpoints(), verifier: verifier, verifierId: verifierId, idToken: idToken, extraParams: buffer) - var data = userData - data["privateKey"] = responseFromRetrieveShares.privateKey - data["publicAddress"] = responseFromRetrieveShares.publicAddress - data["nonce"] = responseFromRetrieveShares.nonce - data["typeOfUser"] = responseFromRetrieveShares.typeOfUser - return data + let nodeDetails = try await nodeDetailManager.getNodeDetails(verifier: verifier, verifierID: verifierId) + // retrieveShares internall checks if network is legacy and calls getPublicAddress if required. + let responseFromRetrieveShares : TorusKey = try await torusUtils.retrieveShares(endpoints: nodeDetails.torusNodeEndpoints, torusNodePubs: nodeDetails.torusNodePub, indexes: nodeDetails.torusIndexes, verifier: verifier, verifierParams: verifierParams, idToken: idToken, extraParams: extraParams) + + return responseFromRetrieveShares } catch { os_log("handleSingleLogin: err: %s", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error, error.localizedDescription) throw error @@ -207,17 +238,20 @@ open class CustomAuth { /// - verifierId: The unique identifier to publicly represent a user on a verifier. e.g: email, sub etc. other fields can be classified as verifierId, /// - subVerifierDetails: An array of verifiers to be used for the aggregate login flow, with their respective token and verifier name. /// - Returns: A promise that resolve with a Dictionary that contain at least `privateKey` and `publicAddress` field.. - open func getAggregateTorusKey(verifier: String, verifierId: String, idToken: String, subVerifierDetails: SubVerifierDetails, userData: [String: Any] = [:]) async throws -> [String: Any] { - let extraParams = ["verifieridentifier": verifier, "verifier_id": verifierId, "sub_verifier_ids": [subVerifierDetails.verifier], "verify_params": [["verifier_id": verifierId, "idtoken": idToken]]] as [String: Any] + open func getAggregateTorusKey(verifier: String, verifierId: String, idToken: String, subVerifierDetails: SubVerifierDetails, userData: [String: Any] = [:]) async throws -> TorusKey { + let extraParams = ["verifieridentifier": verifier, "verifier_id": verifierId, "sub_verifier_ids": [subVerifierDetails.verifier], "verify_params": [["verifier_id": verifierId, "idtoken": idToken]]] as [String: Codable] let buffer: Data = try! NSKeyedArchiver.archivedData(withRootObject: extraParams, requiringSecureCoding: false) let hashedOnce = idToken.sha3(.keccak256) + + let verifierParams = VerifierParams(verifier_id: verifierId) + do { - let nodeDetails = try await getNodeDetailsFromContract(verifier: verifier, verfierID: verifierId) - let responseFromRetrieveShares = try await self.torusUtils.retrieveShares(torusNodePubs: nodeDetails.getTorusNodePub(), endpoints: nodeDetails.getTorusNodeEndpoints(), verifier: verifier, verifierId: verifierId, idToken: hashedOnce, extraParams: buffer) - var data = userData - data["privateKey"] = responseFromRetrieveShares.privateKey - data["publicAddress"] = responseFromRetrieveShares.publicAddress - return data + let nodeDetails = try await getNodeDetailsFromContract(verifier: verifier, verfierID: verifierId) + + // retrieveShares internall checks if network is legacy and calls getPublicAddress if required. + let responseFromRetrieveShares :TorusKey = try await self.torusUtils.retrieveShares(endpoints: nodeDetails.torusNodeEndpoints, torusNodePubs: nodeDetails.torusNodePub, indexes: nodeDetails.torusIndexes, verifier: verifier, verifierParams: verifierParams, idToken: hashedOnce, extraParams: extraParams) + + return responseFromRetrieveShares } catch { os_log("handleSingleIdVerifier err: %@", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error, error.localizedDescription) throw error diff --git a/Sources/CustomAuth/CustomAuthFactory.swift b/Sources/CustomAuth/CustomAuthFactory.swift index 09af25e..9eaf432 100644 --- a/Sources/CustomAuth/CustomAuthFactory.swift +++ b/Sources/CustomAuth/CustomAuthFactory.swift @@ -9,41 +9,42 @@ import FetchNodeDetails import Foundation import OSLog import TorusUtils +import CommonSources /// A protocol should be implmented by users of `CustomAuth`. It provides a way /// to stub or mock the CustomAuth for testing. public protocol CASDKFactoryProtocol { - func createTorusUtils(loglevel: OSLogType, urlSession: URLSession, enableOneKey: Bool, network: EthereumNetworkFND) -> AbstractTorusUtils - func createFetchNodeDetails(network: EthereumNetworkFND, urlSession: URLSession, networkUrl: String?) -> FetchNodeDetails + func createTorusUtils(loglevel: OSLogType, urlSession: URLSession, enableOneKey: Bool, network: TorusNetwork) -> AbstractTorusUtils +// func createFetchNodeDetails(network: TorusNetwork, urlSession: URLSession, networkUrl: String?) -> AllNodeDetailsModel } -public class CASDKFactory: CASDKFactoryProtocol { - public func createFetchNodeDetails(network: EthereumNetworkFND, urlSession: URLSession = URLSession.shared, networkUrl: String? = nil) -> FetchNodeDetails { - var proxyAddress: String = "" - switch network { - case .MAINNET: - proxyAddress = FetchNodeDetails.proxyAddressMainnet - case .TESTNET: - proxyAddress = FetchNodeDetails.proxyAddressTestnet - case .CYAN: - proxyAddress = FetchNodeDetails.proxyAddressCyan - case .AQUA: - proxyAddress = FetchNodeDetails.proxyAddressAqua - case .CELESTE: - proxyAddress = FetchNodeDetails.proxyAddressCeleste - default: - proxyAddress = FetchNodeDetails.proxyAddressMainnet - } - guard let networkUrl = networkUrl else { return FetchNodeDetails(proxyAddress: proxyAddress, network: network, urlSession: urlSession) } - return FetchNodeDetails(proxyAddress: proxyAddress, network: .CUSTOM(path: networkUrl), urlSession: urlSession) - } - - public func createTorusUtils(loglevel: OSLogType, urlSession: URLSession = URLSession.shared, enableOneKey: Bool, network: EthereumNetworkFND) -> AbstractTorusUtils { - let allowHost = network.signerMap.appending("/api/allow") - let signerHost = network.signerMap.appending("/api/sign") - return TorusUtils(loglevel: loglevel, urlSession: urlSession, enableOneKey: enableOneKey, signerHost: signerHost, allowHost: allowHost, network: network) - } - - public init() { - } -} +//public class CASDKFactory: CASDKFactoryProtocol { +// public func createFetchNodeDetails(network: TorusNetwork, urlSession: URLSession = URLSession.shared, networkUrl: String? = nil) -> FetchNodeDetails { +// var proxyAddress: String = "" +// switch network { +// case .MAINNET: +// proxyAddress = FetchNodeDetails.proxyAddressMainnet +// case .TESTNET: +// proxyAddress = FetchNodeDetails.proxyAddressTestnet +// case .CYAN: +// proxyAddress = FetchNodeDetails.proxyAddressCyan +// case .AQUA: +// proxyAddress = FetchNodeDetails.proxyAddressAqua +// case .CELESTE: +// proxyAddress = FetchNodeDetails.proxyAddressCeleste +// default: +// proxyAddress = FetchNodeDetails.proxyAddressMainnet +// } +// guard let networkUrl = networkUrl else { return FetchNodeDetails(proxyAddress: proxyAddress, network: network, urlSession: urlSession) } +// return FetchNodeDetails(proxyAddress: proxyAddress, network: .CUSTOM(path: networkUrl), urlSession: urlSession) +// } +// +// public func createTorusUtils(loglevel: OSLogType, urlSession: URLSession = URLSession.shared, enableOneKey: Bool, network: TorusNetwork) -> AbstractTorusUtils { +// let allowHost = network.signerMap.appending("/api/allow") +// let signerHost = network.signerMap.appending("/api/sign") +// return TorusUtils(loglevel: loglevel, urlSession: urlSession, enableOneKey: enableOneKey, signerHost: signerHost, allowHost: allowHost, network: network) +// } +// +// public init() { +// } +//} diff --git a/Sources/CustomAuth/Extension/CASDK+extension.swift b/Sources/CustomAuth/Extension/CASDK+extension.swift index 172c2fd..0240adf 100644 --- a/Sources/CustomAuth/Extension/CASDK+extension.swift +++ b/Sources/CustomAuth/Extension/CASDK+extension.swift @@ -56,6 +56,29 @@ extension CustomAuth { } } + + public func observeCallbackWithError(_ block: @escaping (_ url: URL?, _ err: String?) -> Void) { + self.observer = CustomAuth.notificationCenter.addObserver( + forName: CustomAuth.didHandleCallbackURL, + object: nil, + queue: OperationQueue.main) { [weak self] notification in + self?.removeCallbackNotificationObserver() + os_log("notification.userInfo: %s", log: getTorusLogger(log: CASDKLogger.core, type: + .info), type: .info, notification.userInfo.debugDescription) + if let errorFromUserInfo = notification.userInfo?["ERROR"] as? String { + os_log("executing error callback block", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error) + block(nil, errorFromUserInfo) + } + else if let urlFromUserInfo = notification.userInfo?["URL"] as? URL { + os_log("executing callback block", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .info) + block(urlFromUserInfo, nil) + + } else { + assertionFailure() + } + } + } + @MainActor public func openURL(url: String, view: UIViewController?, modalPresentationStyle: UIModalPresentationStyle) { os_log("opening URL: %s", log: getTorusLogger(log: CASDKLogger.core, type: .info), type: .info, url) switch authorizeURLHandler { @@ -90,6 +113,12 @@ extension CustomAuth { let notification = Notification(name: CustomAuth.didHandleCallbackURL, object: nil, userInfo: ["URL": url]) notificationCenter.post(notification) } + + open class func handleError(err: String) { + // CustomAuth.logger.info("Posting notification after Universal link/deep link flow") + let notification = Notification(name: CustomAuth.didHandleCallbackURL, object: nil, userInfo: ["ERROR": err]) + notificationCenter.post(notification) + } public func parseURL(url: URL) -> [String: String] { var responseParameters = [String: String]() diff --git a/Sources/CustomAuth/Extension/EthereumNetworkFND+extension.swift b/Sources/CustomAuth/Extension/EthereumNetworkFND+extension.swift index 5520ff0..e409e1b 100644 --- a/Sources/CustomAuth/Extension/EthereumNetworkFND+extension.swift +++ b/Sources/CustomAuth/Extension/EthereumNetworkFND+extension.swift @@ -7,22 +7,35 @@ import FetchNodeDetails import Foundation +import CommonSources -extension EthereumNetworkFND { +extension TorusNetwork { public var signerMap: String { switch self { - case .MAINNET: - return "https://signer.tor.us" - case .TESTNET: - return "https://signer.tor.us" - case .CYAN: - return "https://signer-polygon.tor.us" - case .AQUA: - return "https://signer-polygon.tor.us" - case .CELESTE: - return "https://signer-polygon.tor.us" - case let .CUSTOM(path): - return "https://signer.tor.us" + case .legacy(let network) : + switch network { + case .MAINNET: + return "https://signer.tor.us" + case .TESTNET: + return "https://signer.tor.us" + case .CYAN: + return "https://signer-polygon.tor.us" + case .AQUA: + return "https://signer-polygon.tor.us" + case .CELESTE: + return "https://signer-polygon.tor.us" + case let .CUSTOM(path): + return "https://signer.tor.us" + } + case .sapphire(let network) : + switch network { + + case .SAPPHIRE_MAINNET: + return "https://signer.tor.us" + case .SAPPHIRE_DEVNET: + return "https://signer.tor.us" + + } } } } diff --git a/Sources/CustomAuth/Helpers/ASWebAuthSession.swift b/Sources/CustomAuth/Helpers/ASWebAuthSession.swift index c0bcb2c..3b66d6d 100644 --- a/Sources/CustomAuth/Helpers/ASWebAuthSession.swift +++ b/Sources/CustomAuth/Helpers/ASWebAuthSession.swift @@ -25,6 +25,8 @@ open class ASWebAuthSession: NSObject, TorusURLHandlerTypes { let callbackURL = callbackURL else { print(authError?.localizedDescription as? String ?? "") + let errStr = authError?.localizedDescription as? String ?? "Something went wrong!!" + CustomAuth.handleError(err: errStr) return } CustomAuth.handle(url: callbackURL) diff --git a/Tests/CustomAuthTests/CustomAuthTests.swift b/Tests/CustomAuthTests/CustomAuthTests.swift index cc5af65..0545035 100644 --- a/Tests/CustomAuthTests/CustomAuthTests.swift +++ b/Tests/CustomAuthTests/CustomAuthTests.swift @@ -22,21 +22,25 @@ final class MockSDKTest: XCTestCase { let subVerifier = [SubVerifierDetails(loginProvider: .jwt, clientId: fakeData.generateVerifier(), verifier: expectedVerifier, redirectURL: fakeData.generateVerifier())] let factory = MockFactory() - let CustomAuth = CustomAuth(aggregateVerifierType: .singleLogin, aggregateVerifier: expectedVerifier, subVerifierDetails: subVerifier, factory: factory) - var mockTorusUtils = CustomAuth.torusUtils as! MockAbstractTorusUtils - +// let CustomAuth = CustomAuth(aggregateVerifierType: .singleLogin, aggregateVerifier: expectedVerifier, subVerifierDetails: subVerifiery) + let CustomAuth = CustomAuth(aggregateVerifierType: .singleLogin, aggregateVerifier: expectedVerifier, subVerifierDetails: []) +// var mockTorusUtils = CustomAuth.torusUtils as! MockAbstractTorusUtils + var mockTorusUtils = MockTorusUtils() + CustomAuth.torusUtils = mockTorusUtils // Set Mock data mockTorusUtils.retrieveShares_output["privateKey"] = expectedPrivateKey mockTorusUtils.retrieveShares_output["publicAddress"] = expectedPublicAddress do { - let nodeDetails = try await CustomAuth.getNodeDetailsFromContract(verifier: expectedVerifier, verfierID: expectedVerfierId) - let data = try await CustomAuth.getTorusKey(verifier: expectedVerifier, verifierId: expectedVerfierId, idToken: fakeData.generateVerifier()) - let mockTorusUtils = CustomAuth.torusUtils as! MockAbstractTorusUtils - XCTAssertEqual(mockTorusUtils.retrieveShares_input["endpoints"] as? [String], nodeDetails.getTorusNodeEndpoints()) - XCTAssertEqual(mockTorusUtils.retrieveShares_input["verifierIdentifier"] as? String, expectedVerifier) - XCTAssertEqual(mockTorusUtils.retrieveShares_input["verifierId"] as? String, expectedVerfierId) - XCTAssertEqual(data["privateKey"] as? String, expectedPrivateKey) - XCTAssertEqual(data["publicAddress"] as? String, expectedPublicAddress) + let nodeDetails = try await CustomAuth.getNodeDetailsFromContract(verifier: expectedVerifier, verfierID: expectedVerfierId) + let data = try await CustomAuth.getTorusKey(verifier: expectedVerifier, verifierId: expectedVerfierId, idToken: fakeData.generateVerifier()) + let mockTorusUtils = CustomAuth.torusUtils as! MockAbstractTorusUtils + let FinalKeyData = data.finalKeyData! + print(FinalKeyData) + XCTAssertEqual(mockTorusUtils.retrieveShares_input["endpoints"] as? [String], nodeDetails.getTorusNodeEndpoints()) + XCTAssertEqual(mockTorusUtils.retrieveShares_input["verifierIdentifier"] as? String, expectedVerifier) + XCTAssertEqual(mockTorusUtils.retrieveShares_input["verifierId"] as? String, expectedVerfierId) + XCTAssertEqual(FinalKeyData.privKey, expectedPrivateKey) + XCTAssertEqual(FinalKeyData.evmAddress, expectedPublicAddress) expectation.fulfill() } catch { XCTFail(error.localizedDescription) @@ -53,23 +57,27 @@ final class MockSDKTest: XCTestCase { let expectedVerfierId = fakeData.generateRandomEmail(of: 6) let subVerifier = [SubVerifierDetails(loginProvider: .jwt, clientId: fakeData.generateVerifier(), verifier: expectedVerifier, redirectURL: fakeData.generateVerifier())] - let factory = MockFactory() - let CustomAuth = CustomAuth(aggregateVerifierType: .singleIdVerifier, aggregateVerifier: expectedVerifier, subVerifierDetails: subVerifier, factory: factory) - var mockTorusUtils = CustomAuth.torusUtils as! MockAbstractTorusUtils + let CustomAuth = CustomAuth(aggregateVerifierType: .singleIdVerifier, aggregateVerifier: expectedVerifier, subVerifierDetails: subVerifier) +// var mockTorusUtils = CustomAuth.torusUtils as! MockAbstractTorusUtils + let mockTorusUtils = MockTorusUtils() + CustomAuth.torusUtils = mockTorusUtils // Set Mock data mockTorusUtils.retrieveShares_output["privateKey"] = expectedPrivateKey mockTorusUtils.retrieveShares_output["publicAddress"] = expectedPublicAddress do { - let nodeDetails = try await CustomAuth.getNodeDetailsFromContract(verifier: expectedVerifier, verfierID: expectedVerfierId) - let data = try await CustomAuth.getAggregateTorusKey(verifier: expectedVerifier, verifierId: expectedVerfierId, idToken: fakeData.generateVerifier(), subVerifierDetails: subVerifier[0]) - let mockTorusUtils = CustomAuth.torusUtils as! MockAbstractTorusUtils - XCTAssertEqual(mockTorusUtils.retrieveShares_input["endpoints"] as? [String], nodeDetails.getTorusNodeEndpoints()) - XCTAssertEqual(mockTorusUtils.retrieveShares_input["verifierIdentifier"] as? String, expectedVerifier) - XCTAssertEqual(mockTorusUtils.retrieveShares_input["verifierId"] as? String, expectedVerfierId) - XCTAssertEqual(data["privateKey"] as? String, expectedPrivateKey) - XCTAssertEqual(data["publicAddress"] as? String, expectedPublicAddress) + + let nodeDetails = try await CustomAuth.getNodeDetailsFromContract(verifier: expectedVerifier, verfierID: expectedVerfierId) + let data = try await CustomAuth.getAggregateTorusKey(verifier: expectedVerifier, verifierId: expectedVerfierId, idToken: fakeData.generateVerifier(), subVerifierDetails: subVerifier[0]) + print("Data", data) + let FinalKeyData = data.finalKeyData! + let mockTorusUtils = CustomAuth.torusUtils as! MockAbstractTorusUtils + XCTAssertEqual(mockTorusUtils.retrieveShares_input["endpoints"] as? [String], nodeDetails.getTorusNodeEndpoints()) + XCTAssertEqual(mockTorusUtils.retrieveShares_input["verifierIdentifier"] as? String, expectedVerifier) + XCTAssertEqual(mockTorusUtils.retrieveShares_input["verifierId"] as? String, expectedVerfierId) + XCTAssertEqual(FinalKeyData.privKey, expectedPrivateKey) + XCTAssertEqual(FinalKeyData.evmAddress, expectedPublicAddress) expectation.fulfill() } catch { XCTFail(error.localizedDescription) diff --git a/Tests/CustomAuthTests/IntegrationTests.swift b/Tests/CustomAuthTests/IntegrationTests.swift index 17d4167..cda9504 100644 --- a/Tests/CustomAuthTests/IntegrationTests.swift +++ b/Tests/CustomAuthTests/IntegrationTests.swift @@ -23,7 +23,7 @@ final class IntegrationTests: XCTestCase { redirectURL: "com.googleusercontent.apps.238941746713-vfap8uumijal4ump28p9jd3lbe6onqt4:/oauthredirect", browserRedirectURL: "https://scripts.toruswallet.io/redirect.html") - IntegrationTests.sdk = CustomAuth(aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-test-ios-public", subVerifierDetails: [sub], network: .CYAN) + IntegrationTests.sdk = CustomAuth(aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-test-ios-public", subVerifierDetails: [sub], network: .legacy(.CYAN)) } func test_getTorusKey() async { @@ -34,9 +34,51 @@ final class IntegrationTests: XCTestCase { let jwt = try! generateIdToken(email: email) do { let data = try await IntegrationTests.sdk?.getTorusKey(verifier: TORUS_TEST_VERIFIER, verifierId: email, idToken: jwt) - XCTAssertEqual(data?["publicAddress"] as! String, "0x8AA6C8ddCD868873120aA265Fc63E3a2180375BA") + let finalKeyDataDict = data!.finalKeyData! + let evmAddress = finalKeyDataDict.evmAddress + XCTAssertEqual(evmAddress, "0x8AA6C8ddCD868873120aA265Fc63E3a2180375BA") exp1.fulfill() } catch { + print(error) + XCTFail(error.localizedDescription) + } + wait(for: [exp1], timeout: 15) + } + + + + let TORUS_TEST_EMAIL = "saasas@tr.us"; + let TORUS_IMPORT_EMAIL = "importeduser2@tor.us"; + + let TORUS_EXTENDED_VERIFIER_EMAIL = "testextenderverifierid@example.com"; + + let TORUS_TEST_VERIFIER = "torus-test-health"; + + let TORUS_TEST_AGGREGATE_VERIFIER = "torus-test-health-aggregate"; + let HashEnabledVerifier = "torus-test-verifierid-hash"; + + func test_Sapphire_getTorusKey() async { + + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .google, + clientId: "221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com", + verifier: "google-lrc", + redirectURL: "com.googleusercontent.apps.238941746713-vfap8uumijal4ump28p9jd3lbe6onqt4:/oauthredirect", + browserRedirectURL: "https://scripts.toruswallet.io/redirect.html") + let sdk = CustomAuth(aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-test-ios-public", subVerifierDetails: [sub], network: .sapphire(.SAPPHIRE_DEVNET)) + + + let exp1 = XCTestExpectation(description: "Should be able to get key") + let email = "hello@tor.us" + let jwt = try! generateIdToken(email: TORUS_TEST_EMAIL) + do { + let data = try await sdk.getTorusKey(verifier: TORUS_TEST_VERIFIER, verifierId: TORUS_TEST_EMAIL, idToken: jwt) + let finalKeyDataDict = data.finalKeyData! + let evmAddress = finalKeyDataDict.evmAddress + XCTAssertEqual(evmAddress, "0x4924F91F5d6701dDd41042D94832bB17B76F316F") + exp1.fulfill() + } catch { + print(error) XCTFail(error.localizedDescription) } wait(for: [exp1], timeout: 15) diff --git a/Tests/CustomAuthTests/MockCASDKFactory.swift b/Tests/CustomAuthTests/MockCASDKFactory.swift index 547808f..76bea80 100644 --- a/Tests/CustomAuthTests/MockCASDKFactory.swift +++ b/Tests/CustomAuthTests/MockCASDKFactory.swift @@ -10,16 +10,16 @@ import FetchNodeDetails import Foundation import OSLog import TorusUtils - +import CommonSources public class MockFactory: CASDKFactoryProtocol { init() {} - public func createTorusUtils(loglevel: OSLogType, urlSession: URLSession, enableOneKey: Bool, network: EthereumNetworkFND) -> AbstractTorusUtils { + public func createTorusUtils(loglevel: OSLogType, urlSession: URLSession, enableOneKey: Bool, network: TorusNetwork) -> AbstractTorusUtils { MockTorusUtils() } - public func createFetchNodeDetails(network: EthereumNetworkFND, urlSession: URLSession, networkUrl: String? = nil) -> FetchNodeDetails { - let net = network == .MAINNET ? "0xf20336e16B5182637f09821c27BDe29b0AFcfe80" : "0x6258c9d6c12ed3edda59a1a6527e469517744aa7" - return FetchNodeDetails(proxyAddress: net, network: network) - } +// public func createFetchNodeDetails(network: TorusNetwork, urlSession: URLSession, networkUrl: String? = nil) -> AllNodeDetailsModel { +// let net = network == .MAINNET ? "0xf20336e16B5182637f09821c27BDe29b0AFcfe80" : "0x6258c9d6c12ed3edda59a1a6527e469517744aa7" +// return AllNodeDetailsModel(proxyAddress: net, network: network) +// } } diff --git a/Tests/CustomAuthTests/MockTorusUtils.swift b/Tests/CustomAuthTests/MockTorusUtils.swift index 788d703..c056189 100644 --- a/Tests/CustomAuthTests/MockTorusUtils.swift +++ b/Tests/CustomAuthTests/MockTorusUtils.swift @@ -1,6 +1,7 @@ import CustomAuth import FetchNodeDetails import Foundation +import CommonSources import BigInt @testable import TorusUtils @@ -11,27 +12,29 @@ public protocol MockAbstractTorusUtils { } class MockTorusUtils: AbstractTorusUtils, MockAbstractTorusUtils { - func retrieveShares(torusNodePubs: [TorusNodePubModel], endpoints: [String], verifier: String, verifierId: String, idToken: String, extraParams: Data) async throws -> RetrieveSharesResponseModel { + + func retrieveShares(endpoints: [String], torusNodePubs: [CommonSources.TorusNodePubModel], indexes: [BigUInt], verifier: String, verifierParams: VerifierParams, idToken: String, extraParams: [String : Codable]) async throws -> TorusKey { retrieveShares_input = [ "endpoints": endpoints, "verifierIdentifier": verifier, - "verifierId": verifierId, + "verifierId": verifierParams.verifier_id, "idToken": idToken, "extraParams": extraParams ] - return .init(publicKey: retrieveShares_output["publicAddress"] ?? "", privateKey: retrieveShares_output["privateKey"] ?? "", nonce: BigUInt(0), typeOfUser: .v1) + let finalKeyData : TorusKey.FinalKeyData = .init(evmAddress: retrieveShares_output["publicAddress"] ?? "", X: "", Y: "", privKey: retrieveShares_output["privateKey"] ?? "") + return TorusKey(finalKeyData: finalKeyData, oAuthKeyData: nil, sessionData: nil, metadata: nil, nodesData: nil) } - func getPublicAddress(endpoints: [String], torusNodePubs: [TorusNodePubModel], verifier: String, verifierId: String, isExtended: Bool) async throws -> GetPublicAddressModel { - return .init(address: "") + func getPublicAddress(endpoints: [String], torusNodePubs: [CommonSources.TorusNodePubModel], verifier: String, verifierId: String, extendedVerifierId: String?) async throws -> TorusPublicKey { + return .init(finalKeyData: nil, oAuthKeyData: nil, metadata: nil, nodesData: nil) } - func getUserTypeAndAddress(endpoints: [String], torusNodePub: [TorusNodePubModel], verifier: String, verifierID: String, doesKeyAssign: Bool)async throws -> GetUserAndAddressModel { + func getUserTypeAndAddress(endpoints: [String], torusNodePubs: [TorusNodePubModel]?, verifier: String, verifierID: String, doesKeyAssign: Bool)async throws -> GetUserAndAddress{ return .init(typeOfUser: .v1, address: "", x: "", y: "") } - func getOrSetNonce(x: String, y: String, privateKey: String?, getOnly: Bool) async throws -> GetOrSetNonceResultModel { - return GetOrSetNonceResultModel(typeOfUser: "v1") + func getOrSetNonce(x: String, y: String, privateKey: String?, getOnly: Bool) async throws -> GetOrSetNonceResult { + return GetOrSetNonceResult(typeOfUser: "v1") } var retrieveShares_input: [String: Any] = [:] diff --git a/Tests/CustomAuthTests/StubURLProtocolTests.swift b/Tests/CustomAuthTests/StubURLProtocolTests.swift index 4cf6b7f..0a6717f 100644 --- a/Tests/CustomAuthTests/StubURLProtocolTests.swift +++ b/Tests/CustomAuthTests/StubURLProtocolTests.swift @@ -11,6 +11,7 @@ import Foundation import OSLog import TorusUtils import XCTest +import CommonSources final class StubURLProtocolTests: XCTestCase { // func testStubURLProtocol() { @@ -50,12 +51,12 @@ public class StubMockTorusUtils: TorusUtils { } public class StubMockCASDKFactory: CASDKFactoryProtocol { - public func createFetchNodeDetails(network: EthereumNetworkFND, urlSession: URLSession, networkUrl: String? = nil) -> FetchNodeDetails { - let net = network == .MAINNET ? "0xf20336e16B5182637f09821c27BDe29b0AFcfe80" : "0x6258c9d6c12ed3edda59a1a6527e469517744aa7" - return FetchNodeDetails(proxyAddress: net, network: network, urlSession: urlSession) - } +// public func createFetchNodeDetails(network: TorusNetwork, urlSession: URLSession, networkUrl: String? = nil) -> FetchNodeDetails { +// let net = network == .MAINNET ? "0xf20336e16B5182637f09821c27BDe29b0AFcfe80" : "0x6258c9d6c12ed3edda59a1a6527e469517744aa7" +// return FetchNodeDetails(proxyAddress: net, network: network, urlSession: urlSession) +// } - public func createTorusUtils(loglevel: OSLogType, urlSession: URLSession, enableOneKey: Bool, network: EthereumNetworkFND) -> AbstractTorusUtils { + public func createTorusUtils(loglevel: OSLogType, urlSession: URLSession, enableOneKey: Bool, network: TorusNetwork) -> AbstractTorusUtils { let allowHost = network.signerMap.appending("/api/allow") let signerHost = network.signerMap.appending("/api/sign") return StubMockTorusUtils(loglevel: loglevel, urlSession: urlSession, enableOneKey: enableOneKey, signerHost: signerHost, allowHost: allowHost)