tKey MPC Swift SDK
This guide will help you make a iOS application using Web3Auth MPC Swift SDK, covering the basic functionality on how to use it.
The Web3Auth's MPC tKey MPC Swift SDK is a client-side library you can use with your iOS app to authenticate users using Web3Auth. This authentication can be achieved using any social logins Web3Auth provides or a custom authentication flow. For using Web3Auth's tKey natively in iOS, Web3Auth provides a tKey iOS SDK written in Swift.
Quick Start
git clone https://github.com/tkey/mpc-ios-example
cd mpc-ios-example
# run project in Xcode
Prerequisites
- iOS 13+
- Xcode 11.4+ / 12.x
- Swift 4.x / 5.x
Installation
Swift Package Manager
Add the tKey MPC Swift SDK
-
In Xcode, with your app project open, navigate to File > Add Packages.
-
When prompted, add the tKey iOS SDK repository:
https://github.com/tkey/tkey-mpc-swift
From the
Dependency Rule
dropdown, selectBranch
and typealpha
as the branch. -
When finished, Xcode will automatically begin resolving and downloading your dependencies in the background.
Add the CustomAuth Swift SDK
-
In Xcode, with your app project open, navigate to File > Add Packages.
-
When prompted, add the CustomAuth Swift SDK repository:
https://github.com/torusresearch/customauth-swift-sdk
From the
Dependency Rule
dropdown, selectBranch
and typealpha
as the branch. -
When finished, Xcode will automatically begin resolving and downloading your dependencies in the background.
Add the TSS Client Swift SDK
-
In Xcode, with your app project open, navigate to File > Add Packages.
-
When prompted, add the CustomAuth Swift SDK repository:
https://github.com/torusresearch/tss-client-swift
From the
Dependency Rule
dropdown, selectExact
and enter1.0.6
as the version. -
When finished, Xcode will automatically begin resolving and downloading your dependencies in the background.
Initialization
Once you have installed the Web3Auth Core Kit tKey SDK, the next step is to initialize it. This involves a few steps, such as initiating the tKey SDK with the service provider and modules.
Configuring Service Provider
Service Provider in tKey
generates a Share A, i.e., the private key share
managed by a wallet service provider via their authentication flows. This share in our
wallet infrastructure refers to the social login aspect, where we associate a
private key share with the user's social login, enabling the seamless login experience.
To configure your service provider, you must use CustomAuth Swift SDK. Please note that this SDK is not automatically installed with tKey iOS SDK, so you must install it first.
Initialization
Initalize the SDK depending on the login you require.
import CustomAuth
let sub = SubVerifierDetails(
loginType: .web, // default .web
loginProvider: "TYPE_OF_LOGIN", // .google, .facebook, .discord and other supported login providers
clientId: "CLIENT_ID",
verifierName: "VERIFIER_NAME",
redirectURL: "tdsdk://tdsdk/oauthCallback",
browserRedirectURL: "https://scripts.toruswallet.io/redirect.html")
let tdsdk = CustomAuth(
aggregateVerifierType: "TYPE_OF_VERIFIER", // singleLogin, singleIdVerifier supported
aggregateVerifierName: "VERIFIER_NAME", // Web3Auth Custom verifier name
subVerifierDetails: [sub],
network: .SAPPHIRE_DEVNET,
enableOneKey: true)
SubVerifierDetails
Parameter | Type | Mandatory | Description |
---|---|---|---|
loginType | SubVerifierType | No | loginType to be used. [ web : default, installed ] |
loginProvider | LoginProviders | Yes | loginProvider to be used. [ google , facebook , twitch , reddit , discord , apple , github , linkedin , kakao , twitter , weibo , line , wechat , email_password , and jwt ] |
clientId | String | Yes | login provider's client Id. |
verifier | String | Yes | Web3Auth verifier name |
redirectURL | String | Yes | It refers to a url for the login flow to redirect into your app, it should have a scheme that is registered by your app, for example com.mycompany.myapp://redirect |
browserRedirectURL | String | No | It refers to a page that the browser should use in the login flow, it should have a http or https scheme. e.g. https://scripts.toruswallet.io/redirect.html |
jwtParams | String | No | Additional JWT parameters to be passed. |
urlSession | URLSession | No | Custom URLSession to be used. |
CustomAuth
Parameter | Type | Mandatory | Description |
---|---|---|---|
aggregateVerifierType | String | Yes | Type of the aggregate verifier. |
aggregateVerifier | String | Yes | Name of the aggregate verifier. |
subVerifierDetails | [SubVerifierDetails ] | Yes | Array of SubVerifierDetails. |
network | TorusNetwork | Yes | Network to be used. [ SAPPHIRE_DEVNET , SAPPHIRE_MAINNET , MAINNET , TESTNET , CYAN , AQUA ] |
enableOneKey | Bool | No | Use one key feature that allows users to have the same account in tKey. Note: This flag shouldn't be changed once set for an account; changing it will lead to a different account. |
networkUrl | String | No | Custom network url to be used. |
Getting User Data from Custom Auth
triggerLogin()
returns a promise that resolve with a Dictionary that containfinalKeyData
field to be used to initialize the tKey.
tdsdk.triggerLogin().done{ data in
print("user data", data)
self.userData = data
}.catch{ err in
print(err)
}
Instantiating tKey
let rss_comm = try RssComm()
let thresholdKey = try? ThresholdKey(
storage_layer: storage_layer,
service_provider: service_provider,
enable_logging: true,
manual_sync: false,
rss_comm: rss_comm)
Parameters
Parameter | Type | Description | Mandatory |
---|---|---|---|
metadata | Metadata | Metadata object containing the metadata details of tKey. | No |
shares | ShareStorePolyIdIndexMap | Array of ShareStore with PolyId. | No |
storage_layer | StorageLayer | Takes in the Storage Provider Instance | No |
service_provider | ServiceProvider | Takes in the Service Provider Instance | No |
local_matadata_transitions | Metadata | Existing local metadata to be used. | No |
last_fetch_cloud_metadata | Metadata | Existing cloud metadata to be used. | No |
enable_logging | Bool | This option is used to specify whether to enable logging or not. | No |
manual_sync | Bool | manual sync provides atomicity to your tkey share. If manual_sync is true, you should sync your local metadata transitions manually to your storage_layer, which means your storage layer doesn’t know the local changes of your tkey unless you manually sync, gives atomicity. Otherwise, If manual_sync is false, then your local metadata changes will be synced automatically to your storage layer. If manual_sync = true and want to synchronize manually, you need to call sync_local_metadata_transitions() manually. | No |
rss_comm | RssComm | RssComm object to be used. RSS client, required for TSS. | No |
Example
let finalKeyData = userData["finalKeyData"] as? [String: Any]
let postboxkey = finalKeyData["privKey"] as? String
let verifier = userData["verifier"] as? String
let verifierId = userData["verifierId"] as? String
let storage_layer = try? StorageLayer(
enable_logging: true,
host_url: "https://metadata.tor.us",
server_time_offset: 2)
let fnd = NodeDetailManager(network: .sapphire(.SAPPHIRE_DEVNET))
let nodeDetails = try await fnd.getNodeDetails(verifier: verifier, verifierID: verifierId)
let service_provider = try? ServiceProvider(
enable_logging: true,
postbox_key: postboxkey,
useTss: true,
verifier: verifier,
verifierId: verifierId,
nodeDetails: nodeDetails )
let rss_comm = try RssComm()
let thresholdKey = try? ThresholdKey(
storage_layer: storage_layer,
service_provider: service_provider,
enable_logging: true,
manual_sync: false,
rss_comm: rss_comm)
The following parameters are required to initialize MPC tKey:
@State private var signatures: [[String: Any]]!
@State private var tssEndpoint: [String]!
let fetchKey = finalKeyData["evmAddress"] as? String
let sessionData = userData["sessionData"] as? [String: Any]
let sessionTokenData = sessionData["sessionTokenData"] as? [SessionToken]
signatures = sessionTokenData.map { token in
return [ "data": Data(hex: token.token)!.base64EncodedString(),
"sig": token.signature ]
}
let torusUtils = TorusUtils(
enableOneKey: true,
network: .sapphire(.SAPPHIRE_DEVNET) )
tssEndpoint = nodeDetails!.torusNodeTSSEndpoints
ThresholdKey
The login with the tKey SDK is a two-step process. First, you need to trigger the login process by
calling the triggerLogin()
function of the CustomAuth SDK. Using the returned information, use the
initialize()
function of the tKey to generate the Threshold Key corresponding to the user.
However, before starting this process, you must set up Custom Authentication on your Web3Auth Dashboard. You must Create a Verifier from the Custom Auth section of the Web3Auth Developer Dashboard with your desired configuration.
For further information on how to set up and use a verifier, please visit the Custom Authentication Documentation.
Triggering Login and Initializing Service Provider
import CustomAuth
let sub = SubVerifierDetails(
loginType: .web, // default .web
loginProvider: "TYPE_OF_LOGIN", // .google, .facebook, .discord and other supported login providers
clientId: "CLIENT_ID",
verifierName: "VERIFIER_NAME",
redirectURL: "tdsdk://tdsdk/oauthCallback",
browserRedirectURL: "https://scripts.toruswallet.io/redirect.html")
let tdsdk = CustomAuth(
aggregateVerifierType: "TYPE_OF_VERIFIER", // singleLogin, singleIdVerifier supported
aggregateVerifierName: "VERIFIER_NAME", // Web3Auth Custom verifier name
subVerifierDetails: [sub],
network: .SAPPHIRE_DEVNET,
enableOneKey: true)
tdsdk.triggerLogin().done{ data in
print("user data", data)
let finalKeyData = data["finalKeyData"] as? [String: Any]
let verifier = data["verifier"] as? String
let verifierId = data["verifierId"] as? String
let postboxkey = finalKeyData["privKey"] as? String
let fnd = NodeDetailManager(network: .sapphire(.SAPPHIRE_DEVNET))
let nodeDetails = try await fnd.getNodeDetails(verifier: verifier, verifierID: verifierId)
let service_provider = try? ServiceProvider(
enable_logging: true,
postbox_key: postboxkey,
useTss: true,
verifier: verifier,
verifierId: verifierId,
nodeDetails: nodeDetails )
}.catch{ err in
print(err)
}
Instantiate tKey
let torusUtils = TorusUtils(
enableOneKey: true,
network: .sapphire(.SAPPHIRE_DEVNET) )
let storage_layer = try? StorageLayer(
enable_logging: true,
host_url: "https://metadata.tor.us",
server_time_offset: 2)
let rss_comm = try RssComm()
let thresholdKey = try? ThresholdKey(
storage_layer: storage_layer,
service_provider: service_provider,
enable_logging: true,
manual_sync: false,
rss_comm: rss_comm)
threshold_key = thresholdKey
Parameters
Parameter | Type | Description | Mandatory |
---|---|---|---|
metadata | Metadata | Metadata object containing the metadata details of tKey. | No |
shares | ShareStorePolyIdIndexMap | Array of ShareStore with PolyId. | No |
storage_layer | StorageLayer | Takes in the Storage Provider Instance | No |
service_provider | ServiceProvider | Takes in the Service Provider Instance | No |
local_matadata_transitions | Metadata | Existing local metadata to be used. | No |
last_fetch_cloud_metadata | Metadata | Existing cloud metadata to be used. | No |
enable_logging | Bool | This option is used to specify whether to enable logging or not. | No |
manual_sync | Bool | manual sync provides atomicity to your tkey share. If manual_sync is true, you should sync your local metadata transitions manually to your storage_layer, which means your storage layer doesn’t know the local changes of your tkey unless you manually sync, gives atomicity. Otherwise, If manual_sync is false, then your local metadata changes will be synced automatically to your storage layer. If manual_sync = true and want to synchronize manually, you need to call sync_local_metadata_transitions() manually. | No |
rss_comm | RssComm | RssComm object to be used. RSS client, required for TSS. | No |
Initialize tKey
threshold_key.initialize(params?)
Once you have triggered the login process, you're ready to initialize the tKey. This will generate a Threshold Key corresponding to your login provider.
Parameters
Parameter | Type | Description | Mandatory |
---|---|---|---|
import_share | String | An optional string representing the import share. | No |
input | ShareStore | An optional ShareStore object representing the input. | No |
never_initialize_new_key | Bool | Do not initialize a new tKey if an existing one is found. | No |
include_local_metadata_transitions | Bool | Proritize existing metadata transitions over cloud fetched transitions. | No |
use_tss | Bool | Whether TSS is used or not. | No |
device_tss_share | String | An optional string representing the device share. | No |
device_tss_index | Int32 | An optional integer representing the device share index. | No |
tss_factor_pub | KeyPoint | An optional KeyPoint object representing the tss factor public key. | No |
guard let key_details = try? await threshold_key.initialize(never_initialize_new_key: false, include_local_metadata_transitions: false) else {
alertContent = "Failed to get key details"
return
}
totalShares = Int(key_details.total_shares)
threshold = Int(key_details.threshold)
get_all_tss_tags()
This function returns all the tags of the tss shares.
let tags = try? await threshold_key.get_all_tss_tags()
TSS Module
get_tss_tag()
This function returns the tss tag.
Function Definition
public static func get_tss_tag(threshold_key: ThresholdKey) throws -> String {
var errorCode: Int32 = -1
let result = withUnsafeMutablePointer(to: &errorCode, { error in
threshold_key_get_tss_tag(threshold_key.pointer, error) })
guard errorCode == 0 else {
throw RuntimeError("Error in get_tss_tag")
}
let string = String(cString: result!)
string_free(result)
return string
}
Parameters
Parameter | Type | Description | Mandatory |
---|---|---|---|
threshold_key | ThresholdKey | ThresholdKey object to be used. |
let selected_tag = try TssModule.get_tss_tag(threshold_key: threshold_key)
get_tss_share()
This function returns the tss share.
Function Definition
public static func get_tss_share(threshold_key: ThresholdKey, tss_tag: String, factorKey: String, threshold: Int32 = 0) async throws -> (String, String) {
if factorKey.count > 66 { throw RuntimeError("Invalid factor Key") }
try await TssModule.set_tss_tag(threshold_key: threshold_key, tss_tag: tss_tag)
var errorCode: Int32 = -1
let curvePointer = UnsafeMutablePointer<Int8>(mutating: (threshold_key.curveN as NSString).utf8String)
let factorKeyPointer = UnsafeMutablePointer<Int8>(mutating: (factorKey as NSString).utf8String)
let result = withUnsafeMutablePointer(to: &errorCode, { error in
threshold_key_get_tss_share(threshold_key.pointer, factorKeyPointer, threshold, curvePointer, error)
})
guard errorCode == 0 else {
throw RuntimeError("Error in ThresholdKey get_tss_share")
}
let string = String(cString: result!)
string_free(result)
let splitString = string.split(separator: ",", maxSplits: 2)
return (String(splitString[0]), String(splitString[1]))
}
Parameters
Parameter | Type | Description | Mandatory |
---|---|---|---|
threshold_key | ThresholdKey | ThresholdKey object to be used. | Yes |
tss_tag | String | A string representing the tss tag. | Yes |
factorKey | String | A string representing the factor key. | Yes |
threshold | Int32 | An integer representing the threshold. | No |
let (tssIndex, tssShare) = try await TssModule.get_tss_share(threshold_key: threshold_key, tss_tag: selected_tag, factorKey: factorKey)