Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/web3auth-passwordless #358

Merged
merged 1 commit into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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