Skip to content

Commit

Permalink
Merge pull request #358 from torusresearch/feat/web3auth-passwordless
Browse files Browse the repository at this point in the history
Feat/web3auth-passwordless
  • Loading branch information
chaitanyapotti authored Aug 29, 2024
2 parents be8d8de + 44ec0e8 commit 5e5575a
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 29 deletions.
19 changes: 6 additions & 13 deletions examples/vue-app/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,11 @@ import {
sapphireDevnetVerifierOptions,
testnetVerifierMap,
testnetVerifierOptions,
TORUS_EMAIL_PASSWORDLESS,
TORUS_SMS_PASSWORDLESS,
TWITTER,
uxModeOptions,
WEB3AUTH_CLIENT_ID,
WEB3AUTH_EMAIL_PASSWORDLESS,
WEB3AUTH_SMS_PASSWORDLESS,
WEIBO,
} from "./config";
import { fetchLatestBlock, signEthMessage, signTypedData_v1 } from "./services/chainHandlers";
Expand Down Expand Up @@ -251,9 +251,9 @@ const isDisplay = (name: string): boolean => {
case "appHeading":
return !!privKey.value;
case "loginHintEmail":
return formData.value.loginProvider === TORUS_EMAIL_PASSWORDLESS;
return formData.value.loginProvider === WEB3AUTH_EMAIL_PASSWORDLESS;
case "loginHintPhone":
return formData.value.loginProvider === TORUS_SMS_PASSWORDLESS;
return formData.value.loginProvider === WEB3AUTH_SMS_PASSWORDLESS;
default: {
return true;
Expand Down Expand Up @@ -288,18 +288,11 @@ const loginToConnectionMap = computed((): Record<string, Record<string, string |
[LINE]: { domain: AUTH_DOMAIN },
[COGNITO]: { domain: COGNITO_AUTH_DOMAIN, identity_provider: "Google", response_type: "token", user_info_endpoint: "userInfo" },
[REDDIT]: { domain: AUTH_DOMAIN, connection: "Reddit", verifierIdField: "name", isVerifierIdCaseSensitive: false },
[TORUS_EMAIL_PASSWORDLESS]: {
domain: "https://develop-passwordless.web3auth.io",
verifierIdField: "name",
isVerifierIdCaseSensitive: false,
[WEB3AUTH_EMAIL_PASSWORDLESS]: {
login_hint,
connection: "email",
},
[TORUS_SMS_PASSWORDLESS]: {
domain: "https://develop-passwordless.web3auth.io",
verifierIdField: "name",
[WEB3AUTH_SMS_PASSWORDLESS]: {
login_hint,
connection: "sms",
},
};
});
Expand Down
28 changes: 14 additions & 14 deletions examples/vue-app/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export const PASSKEYS_REGISTER = "passkeys_register";
export const COGNITO = "cognito";
export const AUTH_DOMAIN = "https://torus-test.auth0.com";
export const COGNITO_AUTH_DOMAIN = "https://torus-test.auth.ap-southeast-1.amazoncognito.com/oauth2/";
export const TORUS_EMAIL_PASSWORDLESS = "torus_email_passwordless";
export const TORUS_SMS_PASSWORDLESS = "torus_sms_passwordless";
export const WEB3AUTH_EMAIL_PASSWORDLESS = "email_passwordless";
export const WEB3AUTH_SMS_PASSWORDLESS = "sms_passwordless";
export const LOCAL_NETWORK = "network";
export const uxModeOptions = Object.values(UX_MODE).map((x) => ({ name: x, value: x }));
export const WEB3AUTH_CLIENT_ID = "BJ6l3_kIQiy6YVL7zDlCcEAvGpGukwFgp-C_0WvNI_fAEeIaoVRLDrV5OjtbZr_zJxbyXFsXMT-yhQiUNYvZWpo";
Expand Down Expand Up @@ -72,15 +72,15 @@ export const testnetVerifierMap = {
clientId: "78i338ev9lkgjst3mfeuih9tsh",
verifier: "demo-cognito-example",
},
[TORUS_EMAIL_PASSWORDLESS]: {
name: "Torus Email Passwordless",
typeOfLogin: "jwt",
[WEB3AUTH_EMAIL_PASSWORDLESS]: {
name: "Web3Auth Email Passwordless",
typeOfLogin: "email_passwordless",
clientId: "P7PJuBCXIHP41lcyty0NEb7Lgf7Zme8Q",
verifier: "torus-auth0-email-passwordless-lrc",
},
[TORUS_SMS_PASSWORDLESS]: {
name: "Torus Sms Passwordless",
typeOfLogin: "jwt",
[WEB3AUTH_SMS_PASSWORDLESS]: {
name: "Web3Auth Sms Passwordless",
typeOfLogin: "sms_passwordless",
clientId: "P7PJuBCXIHP41lcyty0NEb7Lgf7Zme8Q",
verifier: "torus-sms-passwordless-lrc",
},
Expand Down Expand Up @@ -130,15 +130,15 @@ export const sapphireDevnetVerifierMap = {
clientId: "4jK24VpfepWRSe5EMdd2if0RBD55pAuA",
verifier: "web3auth-auth0-sms-passwordless-sapphire-devnet",
},
[TORUS_EMAIL_PASSWORDLESS]: {
name: "Torus Email Passwordless",
typeOfLogin: "jwt",
[WEB3AUTH_EMAIL_PASSWORDLESS]: {
name: "Web3Auth Email Passwordless",
typeOfLogin: "email_passwordless",
clientId: "d84f6xvbdV75VTGmHiMWfZLeSPk8M07C",
verifier: "web3auth-auth0-email-passwordless-sapphire-devnet",
},
[TORUS_SMS_PASSWORDLESS]: {
name: "Torus Sms Passwordless",
typeOfLogin: "jwt",
[WEB3AUTH_SMS_PASSWORDLESS]: {
name: "Web3Auth Sms Passwordless",
typeOfLogin: "sms_passwordless",
clientId: "4jK24VpfepWRSe5EMdd2if0RBD55pAuA",
verifier: "web3auth-auth0-sms-passwordless-sapphire-devnet",
},
Expand Down
5 changes: 5 additions & 0 deletions src/handlers/HandlerFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import MockLoginHandler from "./MockLoginHandler";
import PasskeysHandler from "./PasskeysHandler";
import PasswordlessHandler from "./PasswordlessHandler";
import TwitchHandler from "./TwitchHandler";
import Web3AuthPasswordlessHandler from "./Web3AuthPasswordlessHandler";

const createHandler = (params: CreateHandlerParams): ILoginHandler => {
const { verifier, typeOfLogin, clientId, jwtParams } = params;
Expand All @@ -24,6 +25,10 @@ const createHandler = (params: CreateHandlerParams): ILoginHandler => {
return new TwitchHandler(params);
case LOGIN.DISCORD:
return new DiscordHandler(params);
case LOGIN.EMAIL_PASSWORDLESS:
case LOGIN.SMS_PASSWORDLESS:
if (!login_hint) throw new Error("Invalid params. Missing login_hint for web3auth passwordless login");
return new Web3AuthPasswordlessHandler(params);
case LOGIN.PASSWORDLESS:
if (!domain || !login_hint) throw new Error("Invalid params. Missing domain or login_hint for passwordless login");
return new PasswordlessHandler(params);
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/PasswordlessHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import log from "../utils/loglevel";
import AbstractLoginHandler from "./AbstractLoginHandler";
import { Auth0UserInfo, CreateHandlerParams, LoginWindowResponse, PopupResponse, TorusVerifierResponse } from "./interfaces";

export default class JwtHandler extends AbstractLoginHandler {
export default class PasswordlessHandler extends AbstractLoginHandler {
private readonly SCOPE: string = "openid profile email";

private readonly RESPONSE_TYPE: string = "token id_token";
Expand Down
60 changes: 60 additions & 0 deletions src/handlers/Web3AuthPasswordlessHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import deepmerge from "deepmerge";

import { decodeToken, loginToConnectionMap } from "../utils/helpers";
import AbstractLoginHandler from "./AbstractLoginHandler";
import { Auth0UserInfo, CreateHandlerParams, EMAIL_FLOW, LoginWindowResponse, TorusVerifierResponse } from "./interfaces";

export default class Web3AuthPasswordlessHandler extends AbstractLoginHandler {
private readonly SCOPE: string = "openid profile email";

private readonly RESPONSE_TYPE: string = "token id_token";

private readonly PROMPT: string = "login";

constructor(params: CreateHandlerParams) {
super(params);
this.setFinalUrl();
}

setFinalUrl(): void {
const finalUrl = new URL("https://passwordless.web3auth.io/v6/authorize");
const clonedParams = JSON.parse(JSON.stringify(this.params.jwtParams || {}));
this.params.customState = { ...(this.params.customState || {}), client: this.params.web3AuthClientId };
const finalJwtParams = deepmerge(
{
state: this.state,
client_id: this.params.clientId || this.params.web3AuthClientId,
redirect_uri: this.params.redirect_uri,
nonce: this.nonce,
network: this.params.web3AuthNetwork,
connection: loginToConnectionMap[this.params.typeOfLogin],
web3auth_client_id: this.params.web3AuthClientId,
scope: this.SCOPE,
response_type: this.RESPONSE_TYPE,
prompt: this.PROMPT,
flow_type: clonedParams?.flow_type || EMAIL_FLOW.code,
},
clonedParams
);
Object.keys(finalJwtParams).forEach((key: string) => {
const localKey = key as keyof typeof finalJwtParams;
if (finalJwtParams[localKey]) finalUrl.searchParams.append(localKey, finalJwtParams[localKey]);
});
this.finalURL = finalUrl;
}

async getUserInfo(params: LoginWindowResponse): Promise<TorusVerifierResponse> {
const { idToken } = params;

const decodedToken = decodeToken<Auth0UserInfo>(idToken).payload;
const { name, email, picture } = decodedToken;
return {
profileImage: picture,
name,
email,
verifierId: name.toLowerCase(),
verifier: this.params.verifier,
typeOfLogin: this.params.typeOfLogin,
};
}
}
14 changes: 14 additions & 0 deletions src/handlers/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,13 @@ export interface BaseLoginOptions {
connection?: string;
}

export const EMAIL_FLOW = {
link: "link",
code: "code",
} as const;

export type EMAIL_FLOW_TYPE = (typeof EMAIL_FLOW)[keyof typeof EMAIL_FLOW];

export interface Auth0ClientOptions extends BaseLoginOptions {
/**
* Your Auth0 account domain such as `'example.auth0.com'`,
Expand Down Expand Up @@ -372,6 +379,11 @@ export interface Auth0ClientOptions extends BaseLoginOptions {
* @defaultValue userinfo
* */
user_info_route?: string;

/**
* The flow type for email_passwordless login
*/
flow_type?: EMAIL_FLOW_TYPE;
}

export interface SubVerifierDetails {
Expand All @@ -393,6 +405,8 @@ export interface CreateHandlerParams {
redirectToOpener?: boolean;
jwtParams?: Auth0ClientOptions;
customState?: TorusGenericObject;
web3AuthClientId: string;
web3AuthNetwork: TORUS_NETWORK_TYPE;
}

export interface RedirectResultParams {
Expand Down
9 changes: 9 additions & 0 deletions src/login.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { TORUS_NETWORK_TYPE } from "@toruslabs/constants";
import { NodeDetailManager } from "@toruslabs/fetch-node-details";
import { keccak256, Torus, TorusKey } from "@toruslabs/torus.js";

Expand Down Expand Up @@ -38,6 +39,8 @@ class CustomAuth {
locationReplaceOnRedirect: boolean;
popupFeatures: string;
useDkg?: boolean;
web3AuthClientId: string;
web3AuthNetwork: TORUS_NETWORK_TYPE;
};

torus: Torus;
Expand Down Expand Up @@ -81,6 +84,8 @@ class CustomAuth {
locationReplaceOnRedirect,
popupFeatures,
useDkg,
web3AuthClientId,
web3AuthNetwork: network,
};
const torus = new Torus({
network,
Expand Down Expand Up @@ -146,6 +151,8 @@ class CustomAuth {
jwtParams,
uxMode: this.config.uxMode,
customState,
web3AuthClientId: this.config.web3AuthClientId,
web3AuthNetwork: this.config.web3AuthNetwork,
});
let loginParams: LoginWindowResponse;
if (hash && queryParameters) {
Expand Down Expand Up @@ -209,6 +216,8 @@ class CustomAuth {
jwtParams,
uxMode: this.config.uxMode,
customState,
web3AuthClientId: this.config.web3AuthClientId,
web3AuthNetwork: this.config.web3AuthNetwork,
});
// We let the user login to each verifier in a loop. Don't wait for key derivation here.!
let loginParams: LoginWindowResponse;
Expand Down
2 changes: 2 additions & 0 deletions src/utils/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export const LOGIN = {
LINE: "line",
EMAIL_PASSWORD: "email_password",
PASSWORDLESS: "passwordless",
EMAIL_PASSWORDLESS: "email_passwordless",
SMS_PASSWORDLESS: "sms_passwordless",
JWT: "jwt",
PASSKEYS: "passkeys",
} as const;
Expand Down
6 changes: 5 additions & 1 deletion src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function eventToPromise<T>(emitter: EmitterType): Promise<T> {
}

// These are the default connection names used by auth0
export const loginToConnectionMap = {
export const loginToConnectionMap: Record<string, string> = {
[LOGIN.APPLE]: "apple",
[LOGIN.GITHUB]: "github",
[LOGIN.LINKEDIN]: "linkedin",
Expand All @@ -36,6 +36,8 @@ export const loginToConnectionMap = {
[LOGIN.LINE]: "line",
[LOGIN.EMAIL_PASSWORD]: "Username-Password-Authentication",
[LOGIN.PASSWORDLESS]: "email",
[LOGIN.EMAIL_PASSWORDLESS]: "email",
[LOGIN.SMS_PASSWORDLESS]: "sms",
};

export const padUrlString = (url: URL): string => (url.href.endsWith("/") ? url.href : `${url.href}/`);
Expand Down Expand Up @@ -66,6 +68,8 @@ export const getVerifierId = (
switch (typeOfLogin) {
case LOGIN.PASSWORDLESS:
case LOGIN.EMAIL_PASSWORD:
case LOGIN.EMAIL_PASSWORDLESS:
case LOGIN.SMS_PASSWORDLESS:
return caseSensitiveField(name, isVerifierIdCaseSensitive);
case LOGIN.WEIBO:
case LOGIN.GITHUB:
Expand Down

0 comments on commit 5e5575a

Please sign in to comment.