Skip to content

Commit

Permalink
Merge pull request #357 from torusresearch/feat/refactor
Browse files Browse the repository at this point in the history
CustomAuth refactor
  • Loading branch information
chaitanyapotti authored Aug 29, 2024
2 parents 39a4950 + 5326ba7 commit be8d8de
Show file tree
Hide file tree
Showing 15 changed files with 131 additions and 399 deletions.
3 changes: 2 additions & 1 deletion examples/vue-app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 16 additions & 21 deletions src/handlers/AbstractLoginHandler.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,41 @@
import base64url from "base64url";

import { LOGIN_TYPE, UX_MODE, UX_MODE_TYPE } from "../utils/enums";
import { UX_MODE } from "../utils/enums";
import { broadcastChannelOptions, getTimeout, randomId } from "../utils/helpers";
import log from "../utils/loglevel";
import PopupHandler from "../utils/PopupHandler";
import { Auth0ClientOptions, ILoginHandler, LoginWindowResponse, PopupResponse, TorusGenericObject, TorusVerifierResponse } from "./interfaces";
import { CreateHandlerParams, ILoginHandler, LoginWindowResponse, PopupResponse, TorusVerifierResponse } from "./interfaces";

abstract class AbstractLoginHandler implements ILoginHandler {
public nonce: string = randomId();

public finalURL: URL;

public params: CreateHandlerParams;

// Not using object constructor because of this issue
// https://github.com/microsoft/TypeScript/issues/5326
constructor(
readonly clientId: string,
readonly verifier: string,
readonly redirect_uri: string,
readonly typeOfLogin: LOGIN_TYPE,
readonly uxMode: UX_MODE_TYPE,
readonly redirectToOpener?: boolean,
readonly jwtParams?: Auth0ClientOptions,
readonly customState?: TorusGenericObject
) {}
constructor(params: CreateHandlerParams) {
this.params = params;
}

get state(): string {
return encodeURIComponent(
base64url.encode(
JSON.stringify({
...(this.customState || {}),
...(this.params.customState || {}),
instanceId: this.nonce,
verifier: this.verifier,
typeOfLogin: this.typeOfLogin,
redirectToOpener: this.redirectToOpener || false,
verifier: this.params.verifier,
typeOfLogin: this.params.typeOfLogin,
redirectToOpener: this.params.redirectToOpener || false,
})
)
);
}

async handleLoginWindow(params: { locationReplaceOnRedirect?: boolean; popupFeatures?: string }): Promise<LoginWindowResponse> {
const verifierWindow = new PopupHandler({ url: this.finalURL, features: params.popupFeatures, timeout: getTimeout(this.typeOfLogin) });
if (this.uxMode === UX_MODE.REDIRECT) {
const verifierWindow = new PopupHandler({ url: this.finalURL, features: params.popupFeatures, timeout: getTimeout(this.params.typeOfLogin) });
if (this.params.uxMode === UX_MODE.REDIRECT) {
verifierWindow.redirect(params.locationReplaceOnRedirect);
} else {
const { BroadcastChannel } = await import("@toruslabs/broadcast-channel");
Expand All @@ -59,9 +54,9 @@ abstract class AbstractLoginHandler implements ILoginHandler {
reject(new Error(`Error: ${error}. Info: ${JSON.stringify(ev.data || {})}`));
return;
}
if (ev.data && instanceParams.verifier === this.verifier) {
if (ev.data && instanceParams.verifier === this.params.verifier) {
log.info(ev.data);
if (!this.redirectToOpener && bc) await bc.postMessage({ success: true });
if (!this.params.redirectToOpener && bc) await bc.postMessage({ success: true });
resolve({
accessToken,
idToken: idToken || "",
Expand All @@ -76,7 +71,7 @@ abstract class AbstractLoginHandler implements ILoginHandler {
}
};

if (!this.redirectToOpener) {
if (!this.params.redirectToOpener) {
bc = new BroadcastChannel<{
error: string;
data: PopupResponse;
Expand Down
26 changes: 8 additions & 18 deletions src/handlers/DiscordHandler.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,28 @@
import { get } from "@toruslabs/http-helpers";
import deepmerge from "deepmerge";

import { LOGIN_TYPE, UX_MODE_TYPE } from "../utils/enums";
import AbstractLoginHandler from "./AbstractLoginHandler";
import { Auth0ClientOptions, LoginWindowResponse, TorusGenericObject, TorusVerifierResponse } from "./interfaces";
import { CreateHandlerParams, LoginWindowResponse, TorusVerifierResponse } from "./interfaces";

export default class DiscordHandler extends AbstractLoginHandler {
private readonly RESPONSE_TYPE: string = "token";

private readonly SCOPE: string = "identify email";

constructor(
readonly clientId: string,
readonly verifier: string,
readonly redirect_uri: string,
readonly typeOfLogin: LOGIN_TYPE,
readonly uxMode: UX_MODE_TYPE,
readonly redirectToOpener?: boolean,
readonly jwtParams?: Auth0ClientOptions,
readonly customState?: TorusGenericObject
) {
super(clientId, verifier, redirect_uri, typeOfLogin, uxMode, redirectToOpener, jwtParams, customState);
constructor(params: CreateHandlerParams) {
super(params);
this.setFinalUrl();
}

setFinalUrl(): void {
const finalUrl = new URL("https://discord.com/api/oauth2/authorize");
const clonedParams = JSON.parse(JSON.stringify(this.jwtParams || {}));
const clonedParams = JSON.parse(JSON.stringify(this.params.jwtParams || {}));
const finalJwtParams = deepmerge(
{
state: this.state,
response_type: this.RESPONSE_TYPE,
client_id: this.clientId,
redirect_uri: this.redirect_uri,
client_id: this.params.clientId,
redirect_uri: this.params.redirect_uri,
scope: this.SCOPE,
},
clonedParams
Expand Down Expand Up @@ -64,8 +54,8 @@ export default class DiscordHandler extends AbstractLoginHandler {
name: `${name}#${discriminator}`,
email,
verifierId: id,
verifier: this.verifier,
typeOfLogin: this.typeOfLogin,
verifier: this.params.verifier,
typeOfLogin: this.params.typeOfLogin,
};
}
}
28 changes: 9 additions & 19 deletions src/handlers/FacebookHandler.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,28 @@
import { get } from "@toruslabs/http-helpers";
import deepmerge from "deepmerge";

import { LOGIN_TYPE, UX_MODE_TYPE } from "../utils/enums";
import AbstractLoginHandler from "./AbstractLoginHandler";
import { Auth0ClientOptions, LoginWindowResponse, TorusGenericObject, TorusVerifierResponse } from "./interfaces";
import { CreateHandlerParams, LoginWindowResponse, TorusVerifierResponse } from "./interfaces";

export default class FacebookHandler extends AbstractLoginHandler {
private readonly RESPONSE_TYPE: string = "token";

private readonly SCOPE: string = "public_profile email";

constructor(
readonly clientId: string,
readonly verifier: string,
readonly redirect_uri: string,
readonly typeOfLogin: LOGIN_TYPE,
readonly uxMode: UX_MODE_TYPE,
readonly redirectToOpener?: boolean,
readonly jwtParams?: Auth0ClientOptions,
readonly customState?: TorusGenericObject
) {
super(clientId, verifier, redirect_uri, typeOfLogin, uxMode, redirectToOpener, jwtParams, customState);
constructor(params: CreateHandlerParams) {
super(params);
this.setFinalUrl();
}

setFinalUrl(): void {
const finalUrl = new URL("https://www.facebook.com/v15.0/dialog/oauth");
const clonedParams = JSON.parse(JSON.stringify(this.jwtParams || {}));
const finalUrl = new URL("https://www.facebook.com/v20.0/dialog/oauth");
const clonedParams = JSON.parse(JSON.stringify(this.params.jwtParams || {}));
const finalJwtParams = deepmerge(
{
state: this.state,
response_type: this.RESPONSE_TYPE,
client_id: this.clientId,
redirect_uri: this.redirect_uri,
client_id: this.params.clientId,
redirect_uri: this.params.redirect_uri,
scope: this.SCOPE,
},
clonedParams
Expand All @@ -59,9 +49,9 @@ export default class FacebookHandler extends AbstractLoginHandler {
email,
name,
profileImage: picture.data.url || "",
verifier: this.verifier,
verifier: this.params.verifier,
verifierId: id,
typeOfLogin: this.typeOfLogin,
typeOfLogin: this.params.typeOfLogin,
};
}
}
26 changes: 8 additions & 18 deletions src/handlers/GoogleHandler.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { get } from "@toruslabs/http-helpers";
import deepmerge from "deepmerge";

import { LOGIN_TYPE, UX_MODE_TYPE } from "../utils/enums";
import AbstractLoginHandler from "./AbstractLoginHandler";
import { Auth0ClientOptions, LoginWindowResponse, TorusGenericObject, TorusVerifierResponse } from "./interfaces";
import { CreateHandlerParams, LoginWindowResponse, TorusVerifierResponse } from "./interfaces";

export default class GoogleHandler extends AbstractLoginHandler {
private readonly RESPONSE_TYPE: string = "token id_token";
Expand All @@ -12,30 +11,21 @@ export default class GoogleHandler extends AbstractLoginHandler {

private readonly PROMPT: string = "select_account";

constructor(
readonly clientId: string,
readonly verifier: string,
readonly redirect_uri: string,
readonly typeOfLogin: LOGIN_TYPE,
readonly uxMode: UX_MODE_TYPE,
readonly redirectToOpener?: boolean,
readonly jwtParams?: Auth0ClientOptions,
readonly customState?: TorusGenericObject
) {
super(clientId, verifier, redirect_uri, typeOfLogin, uxMode, redirectToOpener, jwtParams, customState);
constructor(params: CreateHandlerParams) {
super(params);
this.setFinalUrl();
}

setFinalUrl(): void {
const finalUrl = new URL("https://accounts.google.com/o/oauth2/v2/auth");
const clonedParams = JSON.parse(JSON.stringify(this.jwtParams || {}));
const clonedParams = JSON.parse(JSON.stringify(this.params.jwtParams || {}));
const finalJwtParams = deepmerge(
{
state: this.state,
response_type: this.RESPONSE_TYPE,
client_id: this.clientId,
client_id: this.params.clientId,
prompt: this.PROMPT,
redirect_uri: this.redirect_uri,
redirect_uri: this.params.redirect_uri,
scope: this.SCOPE,
nonce: this.nonce,
},
Expand All @@ -60,9 +50,9 @@ export default class GoogleHandler extends AbstractLoginHandler {
email,
name,
profileImage,
verifier: this.verifier,
verifier: this.params.verifier,
verifierId: email.toLowerCase(),
typeOfLogin: this.typeOfLogin,
typeOfLogin: this.params.typeOfLogin,
};
}
}
32 changes: 11 additions & 21 deletions src/handlers/HandlerFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,26 @@ import JwtHandler from "./JwtHandler";
import MockLoginHandler from "./MockLoginHandler";
import PasskeysHandler from "./PasskeysHandler";
import PasswordlessHandler from "./PasswordlessHandler";
import RedditHandler from "./RedditHandler";
import TwitchHandler from "./TwitchHandler";

const createHandler = ({
clientId,
redirect_uri,
typeOfLogin,
verifier,
jwtParams,
redirectToOpener,
uxMode,
customState,
}: CreateHandlerParams): ILoginHandler => {
const createHandler = (params: CreateHandlerParams): ILoginHandler => {
const { verifier, typeOfLogin, clientId, jwtParams } = params;
if (!verifier || !typeOfLogin || !clientId) {
throw new Error("Invalid params. Missing verifier, typeOfLogin or clientId");
}
const { domain, login_hint, id_token, access_token } = jwtParams || {};
switch (typeOfLogin) {
case LOGIN.GOOGLE:
return new GoogleHandler(clientId, verifier, redirect_uri, typeOfLogin, uxMode, redirectToOpener, jwtParams, customState);
return new GoogleHandler(params);
case LOGIN.FACEBOOK:
return new FacebookHandler(clientId, verifier, redirect_uri, typeOfLogin, uxMode, redirectToOpener, jwtParams, customState);
return new FacebookHandler(params);
case LOGIN.TWITCH:
return new TwitchHandler(clientId, verifier, redirect_uri, typeOfLogin, uxMode, redirectToOpener, jwtParams, customState);
case LOGIN.REDDIT:
return new RedditHandler(clientId, verifier, redirect_uri, typeOfLogin, uxMode, redirectToOpener, jwtParams, customState);
return new TwitchHandler(params);
case LOGIN.DISCORD:
return new DiscordHandler(clientId, verifier, redirect_uri, typeOfLogin, uxMode, redirectToOpener, jwtParams, customState);
return new DiscordHandler(params);
case LOGIN.PASSWORDLESS:
if (!domain || !login_hint) throw new Error("Invalid params. Missing domain or login_hint for passwordless login");
return new PasswordlessHandler(clientId, verifier, redirect_uri, typeOfLogin, uxMode, redirectToOpener, jwtParams, customState);
return new PasswordlessHandler(params);
case LOGIN.APPLE:
case LOGIN.GITHUB:
case LOGIN.LINKEDIN:
Expand All @@ -46,13 +35,14 @@ const createHandler = ({
case LOGIN.LINE:
case LOGIN.EMAIL_PASSWORD:
case LOGIN.JWT:
case LOGIN.REDDIT:
if (id_token || access_token) {
return new MockLoginHandler(clientId, verifier, redirect_uri, typeOfLogin, uxMode, redirectToOpener, jwtParams, customState);
return new MockLoginHandler(params);
}
if (!domain) throw new Error("Invalid params for jwt login. Missing domain");
return new JwtHandler(clientId, verifier, redirect_uri, typeOfLogin, uxMode, redirectToOpener, jwtParams, customState);
return new JwtHandler(params);
case LOGIN.PASSKEYS:
return new PasskeysHandler(clientId, verifier, redirect_uri, typeOfLogin, uxMode, redirectToOpener, jwtParams, customState);
return new PasskeysHandler(params);
default:
throw new Error("Unsupported login type");
}
Expand Down
Loading

0 comments on commit be8d8de

Please sign in to comment.