Enabling Implicit Authentication Flow in tKey
If you're looking for using an implicit authentication flow in your application, you can use the
Torus Service Provider SDK to do so. The Torus Service Provider is built on top of the CustomAuth
SDK from Web3Auth, which is an internal library used in the PnP SDKs to provide a common interface
for authentication. Here, you need to do some additional setup to enable the SDK to be able to catch
the authentication redirect.
This guide will help you in setting up the Torus Service Provider SDK in your application. It is a 3 step process in general:
- Instantiating the Torus Service Provider
- Initiating the Service Provider Instance within tKey
- Handling the OAuth Redirection
- Triggering Login
Instantiation
Service Provider in tKey
is used for generating 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.
@tkey/service-provider-torus
- npm
- Yarn
- pnpm
npm install --save @tkey/service-provider-torus
yarn add @tkey/service-provider-torus
pnpm add @tkey/service-provider-torus
Parameters
class TorusServiceProvider extends ServiceProviderBase {
customAuthInstance: CustomAuth;
singleLoginKey: BN;
torusKey: TorusKey;
migratableKey: BN | null;
customAuthArgs: CustomAuthArgs;
constructor({ enableLogging, postboxKey, customAuthArgs }: TorusServiceProviderArgs);
static fromJSON(value: StringifiedType): TorusServiceProvider;
init(params: InitParams): Promise<void>;
triggerLogin(params: SubVerifierDetails): Promise<TorusLoginResponse | null>;
triggerAggregateLogin(params: AggregateLoginParams): Promise<TorusAggregateLoginResponse>;
triggerHybridAggregateLogin(
params: HybridAggregateLoginParams,
): Promise<TorusHybridAggregateLoginResponse>;
toJSON(): StringifiedType;
}
This TorusServiceProvider
constructor takes an object with customAuthArgs
as input. It contains
the following parameters:
customAuthArgs
The customAuthArgs
object is the mandatory object needed to be passed within the
TorusServiceProvider
constructor. It contains the following parameters:
- Table
- Interface
Parameter | Type | Description | Default Value | Mandatory |
---|---|---|---|---|
baseUrl | string | Redirect Uri for OAuth is baseUrl +redirectPathName which means that you must specify baseUrl +redirectPathName as redirect_uri at verifier's interface. | Yes | |
metadataUrl? | string | Specify a custom metadata host. | https://metadata.tor.us | No |
network | TORUS_NETWORK_TYPE | Auth Network to target options: mainnet, testnet, aqua & cyan. | mainnet | No |
networkUrl | string | Network Url to read blockchain data from (eg: infura url) | No | |
enableLogging? | boolean | This option is used to specify whether to enable logging. | false | No |
enableOneKey? | boolean | 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. | false | No |
redirectToOpener? | boolean | For chrome extensions, the general methods for capturing auth redirects don't work. So, we redirect to the window which opens the auth window. | false | No |
redirectPathName? | string | This option is used to specify the url path where user will be redirected after login. Redirect Uri for OAuth is baseUrl/redirectPathName. At verifier's interface, please use baseUrl/redirectPathName as the redirect_uri. | redirect | No |
apiKey? | string | API Key for Web3Auth to enable higher access limits | No | |
uxMode? | UX_MODE_TYPE | Two uxModes are supported:
'REDIRECT' mode is recommended in browsers where popups might get blocked. | popup | No |
locationReplaceOnRedirect? | boolean | Whether to replace the url hash/query params from OAuth at the end of the redirect flow | false | No |
popupFeatures? | string | Features of popup window. Please check https://developer.mozilla.org/en-US/docs/Web/API/Window/open#window_features for further documentation. | No | |
storageServerUrl? | string | Specify a custom storage server url | https://broadcast-server.tor.us | No |
customAuthArgs: CustomAuthArgs;
export interface CustomAuthArgs {
/**
* baseUrl , along with redirectPathName is used to construct the uri of page
* where user will be redirected after login.
*/
baseUrl: string;
/**
* Specify a custom metadata host
* @defaultValue https://metadata.tor.us
*/
metadataUrl?: string;
/**
* Auth Network to target options: mainnet | testnet | cyan | aqua
* @defaultValue mainnet
*/
network?: TORUS_NETWORK_TYPE;
/**
* Network Url to read blockchain data from (eg: infura url)
*/
networkUrl?: string;
/**
* This option is used to specify whether to enable logging
*
* @defaultValue false
*/
enableLogging?: boolean;
/**
* Use one key features
*
* @defaultValue false
*/
enableOneKey?: boolean;
/**
* For chrome extensions, the general methods for capturing auth redirects don't work.
* So, we redirect to the window which opens the auth window.
*
* @defaultValue false
*/
redirectToOpener?: boolean;
/**
* This option is used to specify the url path where user will be
* redirected after login. Redirect Uri for OAuth is baseUrl/redirectPathName.
*
*
* @defaultValue redirect
*
*
*/
redirectPathName?: string;
/**
* API Key for Web3Auth to enable higher access limits
*
*/
apiKey?: string;
/**
* Two uxModes are supported:-
* - `'popup'`: In this uxMode, a popup will be shown to user for login.
* - `'redirect'`: In this uxMode, user will be redirected to a new window tab for login.
*
* @defaultValue `'popup'`
* @remarks
*
* Use of `'REDIRECT'` mode is recommended in browsers where popups might get blocked.
*/
uxMode?: UX_MODE_TYPE;
/**
* Whether to replace the url hash/query params from OAuth at the end of the redirect flow
*
* @defaultValue false
*/
locationReplaceOnRedirect?: boolean;
/**
* Features of popup window. Please check https://developer.mozilla.org/en-US/docs/Web/API/Window/open#window_features
* for further documentation.
*/
popupFeatures?: string;
/**
* Specify a custom storage server url
* @defaultValue https://broadcast-server.tor.us
*/
storageServerUrl?: string;
}
Usage
const web3AuthClientId = "YOUR_WEB3AUTH_CLIENT_ID"; // get from https://dashboard.web3auth.io
// Configuration of Service Provider
const customAuthArgs = {
web3AuthClientId,
baseUrl: `${window.location.origin}/serviceworker`,
network: "sapphire_mainnet", // based on the verifier network.
uxMode: "popup", // or redirect
};
const serviceProvider = new TorusServiceProvider({
enableLogging: false,
customAuthArgs: customAuthArgs as any,
});
Initializing Service Provider
You need to initialize your Service Provider within your constructor function to use it while
logging your user in through the social accounts. This is done by calling the init()
function
within the tKey
instance's serviceProvider
property.
tKey.serviceProvider.init(initParams);
Parameters
- Table
- Interface
Parameter | Type | Description | Default Value |
---|---|---|---|
skipSw? | boolean | Skips the installation / check for service worker | false |
skipInit? | boolean | Skips the init function | false |
skipPrefetch? | boolean | Skips the prefetching of redirect url | false |
interface InitParams {
/**
* skips the installation / check for service worker
* @defaultValue false
*/
skipSw?: boolean;
/**
* skips the init function
* @defaultValue false
*/
skipInit?: boolean;
/**
* skips the prefetching of redirect url
* @defaultValue false
*
*/
skipPrefetch?: boolean;
}
Usage
useEffect(() => {
const init = async () => {
// Initialization of Service Provider
try {
await (tKey.serviceProvider as any).init();
} catch (error) {
console.error(error);
}
};
init();
}, []);
Handling Redirection
In the Implicit Flow, you need to set up a redirect page to capture the authentication redirect. This is needed since the authentication redirect contains the authentication information of the user, which is needed to generate the OAuthKey. The redirect page is used to capture this information and pass it over to the Torus Service Provider.
The uxMode
parameter in the customAuthArgs
object is used to determine the type of redirect page
to be used. There are two types of redirect
- Popup Mode (default): We deploy a service worker to capture the redirect.
- Redirect Mode: You need to create a redirect page to capture the redirect.
Redirect Page
You can get login result by calling getRedirectResult
on redirected page mount. For example, if
baseUrl is http://localhost:3000
and redirectPathName
is auth
then user will be redirected to
http://localhost:3000/auth
page after login where you can get login result by calling
getRedirectResult
on redirected page mount.
Usage
useEffect(() => {
const init = async () => {
// Initialization of Service Provider
try {
// Init is required for Redirect Flow but skip fetching sw.js and redirect.html )
(tKey.serviceProvider as any).init({ skipInit: true });
if (window.location.pathname === "/auth" && window.location.hash.includes("#state")) {
let result = await (tKey.serviceProvider as any).directWeb.getRedirectResult();
tKey.serviceProvider.postboxKey = new BN((result.result as any).privateKey!, "hex");
await tKey.initialize();
}
} catch (error) {
console.error(error);
}
};
init();
}, []);
Service Worker
A service worker is a scripts that is run by the browser. It does not have any direct relationship
with the DOM and provides many out of the box network-related features. Web3Auth Core Kit tKey SDK
needs a service worker relative to baseUrl
to capture the auth redirect at redirectPathName
path.
For example, while using service worker if baseUrl
is http://localhost:3000/serviceworker
then
user will be redirected to http://localhost:3000/serviceworker/redirect
page after login where
service worker will capture the results and send it back to original window where login was
initiated.
-
Using service worker is optional, but highly recommended. You can skip it by passing
skipSw
param while initializing tKey. -
Service worker is needed if you are using
popup
uxMode within your Service Provider configuration. -
For browsers where service workers are not supported, or in the case you wish to not use service workers, create and serve redirect page (i.e
redirect.html
file).
Service Worker Setup
-
If you're using React, to setup service worker, you need to create a
sw.js
file in your public folder and register it in yourindex.html
file. You can find more information about it in this blog. -
For Angular, this guide will be helpful in setting up the service worker.
-
For Vue, this guide is a great way to get started with service workers.
Service Worker Code
You can directly copy the service worker file code from here and paste in your respective folder. You can also find the code in our tKey JS Examples (Popup Flow).
Service Worker Code
/* eslint-disable */
function getScope() {
return self.registration.scope;
}
self.addEventListener("message", function (event) {
if (event.data && event.data.type === "SKIP_WAITING") {
self.skipWaiting();
}
});
self.addEventListener("fetch", function (event) {
try {
const url = new URL(event.request.url);
if (url.pathname.includes("redirect") && url.href.includes(getScope())) {
event.respondWith(
new Response(
new Blob(
[
`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Redirect</title>
<style>
* {
box-sizing: border-box;
}
html,
body {
background: #fcfcfc;
height: 100%;
padding: 0;
margin: 0;
}
.container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
h1.title {
font-size: 14px;
color: #0f1222;
font-family: "Roboto", sans-serif !important;
margin: 0;
text-align: center;
}
.spinner .beat {
background-color: #0364ff;
height: 12px;
width: 12px;
margin: 24px 2px 10px;
border-radius: 100%;
-webkit-animation: beatStretchDelay 0.7s infinite linear;
animation: beatStretchDelay 0.7s infinite linear;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
display: inline-block;
}
.spinner .beat-odd {
animation-delay: 0s;
}
.spinner .beat-even {
animation-delay: 0.35s;
}
@-webkit-keyframes beatStretchDelay {
50% {
-webkit-transform: scale(0.75);
transform: scale(0.75);
-webkit-opacity: 0.2;
opacity: 0.2;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
-webkit-opacity: 1;
opacity: 1;
}
}
@keyframes beatStretchDelay {
50% {
-webkit-transform: scale(0.75);
transform: scale(0.75);
-webkit-opacity: 0.2;
opacity: 0.2;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
-webkit-opacity: 1;
opacity: 1;
}
}
@media (min-width: 768px) {
h1.title {
font-size: 14px;
}
p.info {
font-size: 28px;
}
.spinner .beat {
height: 12px;
width: 12px;
}
}
</style>
</head>
<body>
<div id="message" class="container">
<div class="spinner content" id="spinner">
<div class="beat beat-odd"></div>
<div class="beat beat-even"></div>
<div class="beat beat-odd"></div>
</div>
<h1 class="title content" id="closeText" style="display: none;">You can close this window now</h1>
</div>
<script
src="https://cdn.jsdelivr.net/npm/@toruslabs/broadcast-channel@10.0.2/dist/broadcastChannel.umd.min.js"
integrity="sha256-q78HZzZl8u46uVK0a+t5vzPyAevMwEWHi43ok+P7/O8="
crossorigin="anonymous"
></script>
<script>
function storageAvailable(type) {
var storage;
try {
storage = window[type];
var x = "__storage_test__";
storage.setItem(x, x);
storage.removeItem(x);
return true;
} catch (e) {
return (
e &&
// everything except Firefox
(e.code === 22 ||
// Firefox
e.code === 1014 ||
// test name field too, because code might not be present
// everything except Firefox
e.name === "QuotaExceededError" ||
// Firefox
e.name === "NS_ERROR_DOM_QUOTA_REACHED") &&
// acknowledge QuotaExceededError only if there's something already stored
storage &&
storage.length !== 0
);
}
}
function showCloseText() {
var closeText = document.getElementById("closeText");
var spinner = document.getElementById("spinner");
if (closeText) {
closeText.style.display = "block";
}
if (spinner) {
spinner.style.display = "none";
}
}
var isLocalStorageAvailable = storageAvailable("localStorage");
// set theme
let theme = "light";
if (isLocalStorageAvailable) {
var torusTheme = localStorage.getItem("torus-theme");
if (torusTheme) {
theme = torusTheme.split("-")[0];
}
}
if (theme === "dark") {
document.querySelector("body").style.backgroundColor = "#24252A";
}
var bc;
var broadcastChannelOptions = {
// type: 'localstorage', // (optional) enforce a type, oneOf['native', 'idb', 'localstorage', 'node'
webWorkerSupport: false, // (optional) set this to false if you know that your channel will never be used in a WebWorker (increase performance)
};
var instanceParams = {};
var preopenInstanceId = new URL(window.location.href).searchParams.get("preopenInstanceId");
if (!preopenInstanceId) {
document.getElementById("message").style.visibility = "visible";
// in general oauth redirect
try {
var url = new URL(location.href);
var hash = url.hash.substr(1);
var hashParams = {};
if (hash) {
hashParams = hash.split("&").reduce(function (result, item) {
var parts = item.split("=");
result[parts[0]] = parts[1];
return result;
}, {});
}
var queryParams = {};
for (var key of url.searchParams.keys()) {
queryParams[key] = url.searchParams.get(key);
}
var error = "";
try {
if (Object.keys(hashParams).length > 0 && hashParams.state) {
instanceParams = JSON.parse(base64urlLib.decode(decodeURIComponent(decodeURIComponent(hashParams.state)))) || {};
if (hashParams.error) error = hashParams.error;
} else if (Object.keys(queryParams).length > 0 && queryParams.state) {
instanceParams = JSON.parse(base64urlLib.decode(decodeURIComponent(decodeURIComponent(queryParams.state)))) || {};
if (queryParams.error) error = queryParams.error;
}
} catch (e) {
console.error(e);
}
if (instanceParams.redirectToOpener) {
// communicate to window.opener
window.opener.postMessage(
{
channel: "redirect_channel_" + instanceParams.instanceId,
data: {
instanceParams: instanceParams,
hashParams: hashParams,
queryParams: queryParams,
},
error: error,
},
"http://localhost:3000"
);
} else {
// communicate via broadcast channel
bc = new broadcastChannelLib.BroadcastChannel("redirect_channel_" + instanceParams.instanceId, broadcastChannelOptions);
bc.postMessage({
data: {
instanceParams: instanceParams,
hashParams: hashParams,
queryParams: queryParams,
},
error: error,
}).then(function () {
bc.close();
console.log("posted", {
queryParams,
instanceParams,
hashParams,
});
setTimeout(function () {
window.close();
showCloseText();
}, 5000);
});
}
} catch (err) {
console.error(err, "service worker error in redirect");
bc && bc.close();
window.close();
showCloseText();
}
} else {
// in preopen, awaiting redirect
try {
bc = new broadcastChannelLib.BroadcastChannel("preopen_channel_" + preopenInstanceId, broadcastChannelOptions);
bc.onmessage = function (ev) {
var { preopenInstanceId: oldId, payload, message } = ev.data;
if (oldId === preopenInstanceId && payload && payload.url) {
window.location.href = payload.url;
} else if (oldId === preopenInstanceId && message === "setup_complete") {
bc.postMessage({
data: {
preopenInstanceId: preopenInstanceId,
message: "popup_loaded",
},
});
}
if (ev.error && ev.error !== "") {
console.error(ev.error);
bc.close();
}
};
} catch (err) {
console.error(err, "service worker error in preopen");
bc && bc.close();
window.close();
showCloseText();
}
}
</script>
</body>
</html>
${""}
`,
],
{ type: "text/html" },
),
),
);
}
} catch (error) {
console.error(error);
}
});
Log In
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 Service Provider. Following which 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 need to set up Custom Authentication on your Web3Auth Dashboard. For this, you need to Create a Verifier from the Custom Auth section of the Web3Auth Developer Dashboard with your desired configuration.
If you want to know more about setting up a verifier and how to use it, please refer to the Custom Authentication Documentation.
Triggering Login
tKey.serviceProvider.triggerLogin(SubVerifierDetails)
This is a needed step since this will generate a private key which will be needed by the tKey to
generate it's share. This is done by calling the triggerLogin()
function within the tKey
instance's serviceProvider
.
SubVerifierDetails
The triggerLogin
function in TorusServiceProvider
accepts the following parameters:
- Table
- Interface
Parameter | Type | Description | Mandatory |
---|---|---|---|
typeOfLogin | LOGIN_TYPE | Type of your login verifier | Yes |
verifier | string | Verifier Name from Web3Auth Dashboard | Yes |
clientId | string | Client ID from your login service provider | Yes |
jwtParams? | Auth0ClientOptions | Additional JWT Params | No |
hash? | string | Your JWT in hash | No |
queryParameters? | TorusGenericObject | Additional Query Params | No |
customState? | TorusGenericObject | Additional Custom State Params | No |
interface SubVerifierDetails {
typeOfLogin: LOGIN_TYPE;
verifier: string;
clientId: string;
jwtParams?: Auth0ClientOptions;
hash?: string;
queryParameters?: TorusGenericObject;
customState?: TorusGenericObject;
}
export declare type TorusGenericObject = {
[key: string]: string;
};
jwtParams
- Table
- Interface
Parameter | Type | Description | Mandatory |
---|---|---|---|
domain | string | Domain of your Auth0 App such as 'example.auth0.com'. Please use https://` as a prefix | Yes |
client_id? | string | The Client ID found on your Auth0 Application settings page | Yes |
redirect_uri? | string | The default URL where Auth0 will redirect your browser to with the authentication result. It must be whitelisted in the "Allowed Callback URLs" field in your Auth0 Application's settings. If not provided here, it should be provided in the other methods that provide authentication. | No |
leeway? | number | The value in seconds used to account for clock skew in JWT expirations. Typically, this value is no more than a minute or two at maximum. Defaults to 60s. | No |
verifierIdField? | string | The field in jwt token which maps to verifier id | No |
isVerifierIdCaseSensitive? | boolean | Whether the verifier id field is case sensitive. @defaultValue true | No |
id_token? | string | Pass on the id_token directly here. Useful in case of RWA | No |
access_token? | string | Pass on the access_token directly here. | No |
user_info_route? | string | The route for user info endpoint. This will be padded to domain. @defaultValue userinfo | No |
interface Auth0ClientOptions extends BaseLoginOptions {
/**
* Your Auth0 account domain such as `'example.auth0.com'`,
* `'example.eu.auth0.com'` or , `'example.mycompany.com'`
* (when using [custom domains](https://auth0.com/docs/custom-domains))
*/
domain: string;
/**
* The Client ID found on your Application settings page
*/
client_id?: string;
/**
* The default URL where Auth0 will redirect your browser to with
* the authentication result. It must be whitelisted in
* the "Allowed Callback URLs" field in your Auth0 Application's
* settings. If not provided here, it should be provided in the other
* methods that provide authentication.
*/
redirect_uri?: string;
/**
* The value in seconds used to account for clock skew in JWT expirations.
* Typically, this value is no more than a minute or two at maximum.
* Defaults to 60s.
*/
leeway?: number;
/**
* The field in jwt token which maps to verifier id
*/
verifierIdField?: string;
/**
* Whether the verifier id field is case sensitive
* @defaultValue true
*/
isVerifierIdCaseSensitive?: boolean;
id_token?: string;
access_token?: string;
/**
* The route for user info endpoint. This will be padded to domain
* @defaultValue userinfo
* */
user_info_route?: string;
}
typeOfLogin
export type LOGIN_TYPE = (typeof LOGIN)[keyof typeof LOGIN];
export declare const LOGIN: {
readonly GOOGLE: "google";
readonly FACEBOOK: "facebook";
readonly REDDIT: "reddit";
readonly DISCORD: "discord";
readonly TWITCH: "twitch";
readonly APPLE: "apple";
readonly GITHUB: "github";
readonly LINKEDIN: "linkedin";
readonly TWITTER: "twitter";
readonly WEIBO: "weibo";
readonly LINE: "line";
readonly EMAIL_PASSWORD: "email_password";
readonly PASSWORDLESS: "passwordless";
readonly JWT: "jwt";
readonly WEBAUTHN: "webauthn";
};
Usage
const loginResponse = await(tKey.serviceProvider as any).triggerLogin({
typeOfLogin: "google", // "google","facebook","reddit","discord","twitch", etc.
verifier: "google-tkey-w3a",
clientId: "774338308167-q463s7kpvja16l4l0kko3nb925ikds2p.apps.googleusercontent.com",
});
const user = loginResponse.userInfo;
console.log("User Details: ", user);
Get User Information
triggerLogin(params: SubVerifierDetails): Promise<TorusLoginResponse>;
The tKey Service Provider returns a TorusLoginResponse
object which contains the user's
information and details about the login. You can access the userInfo
property within it to get the
user details from the login provider.
TorusLoginResponse
- Table
- Interface
Parameter | Type | Description |
---|---|---|
accessToken | string | User Access Token |
idToken? | string | User id_token |
ref? | string | User refs |
extraParams? | string | Any extra parameters |
email | string | User Email ID |
name | string | User Name |
profileImage | string | User Profile Image |
aggregateVerifier? | string | Aggregate Verifier Details |
verifier | string | Verifier Details |
verifierId | string | Verifier ID |
typeOfLogin | LOGIN_TYPE | Type of Social Login |
export declare type TorusLoginResponse = TorusSingleVerifierResponse & TorusKey;
export interface TorusSingleVerifierResponse {
userInfo: TorusVerifierResponse & LoginWindowResponse;
}
export interface LoginWindowResponse {
accessToken: string;
idToken?: string;
ref?: string;
extraParams?: string;
extraParamsPassed?: string;
state: TorusGenericObject;
}
export interface TorusVerifierResponse {
email: string;
name: string;
profileImage: string;
aggregateVerifier?: string;
verifier: string;
verifierId: string;
typeOfLogin: LOGIN_TYPE;
ref?: string;
registerOnly?: boolean;
}
TorusKey
- Table
- Interface
Parameter | Type | Description |
---|---|---|
publicAddress | string | User Public Address |
privateKey | string | User Private Key |
metadataNonce | string | Metadata Nonce related to the user |
typeOfUser | enum("v1", "v2") | User if v1 or v2 |
pub_key_X | string | X coordinate of Public Key |
pub_key_Y | string | Y coordinate of Public Key |
export interface TorusKey extends TorusKeyPub {
publicAddress: string;
privateKey: string;
metadataNonce: string;
typeOfUser: "v1" | "v2";
}
export interface TorusKeyPub {
pubKey?: {
pub_key_X: string;
pub_key_Y: string;
};
}
Trigger Login using Aggregate Verifier
triggerAggregateLogin()
await (tKey.serviceProvider as TorusServiceProvider).triggerAggregateLogin(AggregateLoginParams)
Takes in the aggregate verifier details as AggregateLoginParams
.
interface AggregateLoginParams {
aggregateVerifierType: AGGREGATE_VERIFIER_TYPE;
verifierIdentifier: string;
subVerifierDetailsArray: SubVerifierDetails[];
}