Create & Auth
Liquality Wallet API and SDK relies on Torus wallet tech for authentication. This significantly simplifies the onboarding experience and removes risk of key loss as users do not need to keep track of a seed phrase. Instead, authentication is handled by key shares that allow for easy recoverability without compromising security.
Overview
The keys are managed by creating shares of the private key via Shamir Secret Sharing.
ShareNumberOne Could be stored on the user's device, similarly to how you usually can store a private key or seed phrase on a hardware device.
ShareNumberTwo could be split across the Torus Tech Network, only accessed by a OAuth provider login that the user owns. This could be Google SSO, or any of the other login providers that are currently supported (Facebook, Twitch, Discord)
ShareNumberThree This share is a recovery share, which can be accessed through a users set password
Liquality SDK provides different levels of support for the auth and creation.
Full flow is provided by a UI component that can be embedded and you get login flow + full recovery.
Programmatic access: easy to use API for login and recovery but the integration with UI happens by the developer. This gives flexibility for any use case.
Recovery
Users need to prove ownership of ⅔ shares in order to retrieve and recover their private key.
That means if you choose to split your shares between Google SSO, device storage and password, you can recover your account using google SSO + the password for example.
Programmatic Authorization
Create a wallet
In order to create a wallet or do wallet interactions, you first have to initiate the tKey instance.
This is done by calling:
import { AuthService, tryRegisterSW } from "@liquality/wallet-sdk";
// Register a service worker hosted at the root of the site using the default scope.
const registration = tryRegisterSW("/serviceworker/sw.js");
const tKey = AuthService.init(directParams);
The init() function takes an object that we call directParams and it has the following object properties:
Name | Type | Description |
---|---|---|
baseUrl | string | the path to the serviceworker that is being served |
enableLogging | boolean | decides if logging should be enabled |
networkUrl | string | rpcUrl to the network you want to connect to |
network | string | torusNetwork, could be mainnet or testnet |
Example:
const directParams = {
baseUrl: `http://localhost:3005/serviceworker`,
enableLogging: true,
networkUrl: "https://goerli.infura.io/v3/a8684b771e9e4997a567bbd7189e0b27",
network: "testnet" as any,
};
Liquality SDK create & auth functions currently supports all Ethereum networks, Binance Smart Chain, Polygon (Matic), ReefChain and xDai.
Then you can trigger a wallet creation with google SSO. This is done by calling the
const wallet = await AuthService.createWallet(tKey, verifierMap);
Where tKey is the variable that you got from the init() function and the verifierMap is an object with the properties:
const verifierMap: Record<string, any> = {
google: {
name: "Google",
typeOfLogin: "google",
clientId:
"852640103435-0qhvrgpkm66c9hu0co6edkhao3hrjlv3.apps.googleusercontent.com",
verifier: "liquality-google-testnet",
},
};
Name | Type | Description |
---|---|---|
name | string | name of type of SSO login |
typeOfLogin | string | type of SSO login |
clientId | string | client ID that is taken from the SSO, developers portal |
verifier | string | the verifier name from the SSO developers portal |
A clientID and verifier can be created on the Google developers portal. For google SSO with gmail, you can follow the steps to create authorization details: https://developers.google.com/identity/sign-in/web/sign-in
Set a password share
After you created your wallet, you can set a password share for the user.
Parameters:
Name | Type | Description |
---|---|---|
tKey | Thresholdkey | tKey instance from the init() |
password | string | the password the user inputs |
let response = await AuthService.generateNewShareWithPassword(
tKey: ThresholdKey,
password: string
);
Login and unlock wallet
Unlock wallet with single-sign-on This function uses 2 of the shares (the webstorage share that is stored on the browser side) and the share stored on the service provider to log in and unlock your wallet.
const loginResponse = await loginUsingSSO(tKey: ThresholdKey, verifierMap: Record<string, any>)
It expects the tKey that is initilized from the init() function and the verifierMap described previously.
React Example
Create wallet & sign in
import * as React from "react";
import { useState, useEffect } from "react";
import { AuthService, tryRegisterSW } from "@liquality/wallet-sdk";
import { DirectParams } from "sdk/src/types";
type Props = {
directParams: DirectParams,
verifierMap: Record<string, any>,
};
export const CreateWallet: React.FC<Props> = (props) => {
const { directParams, verifierMap } = props;
const [tKey, setTKey] = useState < any > {};
const [loginResponse, setLoginResponse] = useState < any > {};
const [password, setPassword] = useState < string > "";
const [errorMsg, setErrorMsg] = useState < string > "";
const [passwordResponse, setPasswordResponse] = useState < string > "";
const [newPasswordShare, setNewPasswordShare] = useState < any > {};
useEffect(() => {
const init = async () => {
tryRegisterSW("/serviceworker/sw.js");
const tKeyResponse = await AuthService.init(directParams);
setTKey(tKeyResponse);
};
init();
}, [loginResponse, passwordResponse]);
const createNewWallet = async () => {
const response = await AuthService.createWallet(tKey, verifierMap);
setLoginResponse(response);
};
const generatePassword = async (password: string) => {
let response = await AuthService.generateNewShareWithPassword(
loginResponse.tKey,
password
);
setNewPasswordShare(response.result);
response.msg.startsWith("Error")
? setErrorMsg(response.msg)
: setPasswordResponse(response.msg);
};
const _renderPasswordInput = () => {
return (
<div>
Set password minimum 10 characters:
<input
type="password"
placeholder="Address"
value={password}
onChange={(e) => {
setPassword(e.target.value);
}}
/>
<button onClick={() => generatePassword(password)}>Set password</button>
<br></br>
{errorMsg ? <p style={{ color: "red" }}> {errorMsg}</p> : null}
{passwordResponse.startsWith("Error") ? (
<p style={{ color: "red" }}> {passwordResponse}</p>
) : (
<p style={{ color: "green" }}>{passwordResponse}</p>
)}
</div>
);
};
const _renderCreatedWalletDetails = () => {
return (
<div>
<h3 style={{ color: "green" }}>
Your wallet was created successfully!
</h3>
<p>
<b>Public Address:</b> <br></br>
{loginResponse.loginResponse?.publicAddress}
</p>
<p>
<b>Private Key:</b> <br></br>
{loginResponse.loginResponse?.privateKey}
</p>
<p>
<b>User email:</b> <br></br> {loginResponse.loginResponse?.userInfo?.email}
</p>
</div>
);
};
return (
<div style={{ border: "1px solid black", padding: 10 }}>
<h3>Liquality & tKey Create Wallet</h3>
<button onClick={createNewWallet}>Create Wallet</button>
{loginResponse.loginResponse ? _renderCreatedWalletDetails() : null}
{loginResponse.loginResponse ? _renderPasswordInput() : null}
</div>
);
};