From 09c0133cb695a7afc33202779e382574d4e8b8a9 Mon Sep 17 00:00:00 2001 From: rustin01 <141540002+rustin01@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:47:57 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20oauth=20oidc=20popup-based?= =?UTF-8?q?=20redirect=20(#188)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/utils/auth/oauth2-oidc-window.ts | 72 +++++++++++++++++++ .../wallet/src/utils/auth/providers/google.ts | 16 ++++- apps/wallet/src/utils/auth/providers/x.ts | 16 ++++- 3 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 apps/wallet/src/utils/auth/oauth2-oidc-window.ts diff --git a/apps/wallet/src/utils/auth/oauth2-oidc-window.ts b/apps/wallet/src/utils/auth/oauth2-oidc-window.ts new file mode 100644 index 0000000..4f401a9 --- /dev/null +++ b/apps/wallet/src/utils/auth/oauth2-oidc-window.ts @@ -0,0 +1,72 @@ +class OAuth2OidcWindow { + public readonly url: string; + public readonly redirectOrigin: string; + + private window: WindowProxy | null = null + private promise: Promise | null = null + private _iid: number | null = null; + + constructor(url: string, redirectOrigin: string) { + this.url = url; + this.redirectOrigin = redirectOrigin; + } + + open() { + const { url } = this; + this.window = window.open(url, 'oauth2-oidc', 'popup=1,width=800,height=600'); + } + + close() { + this.cancel(); + this.window?.close(); + } + + poll() { + this.promise = new Promise((resolve, reject) => { + this._iid = window.setInterval(() => { + try { + const popup = this.window; + if (!popup || popup.closed !== false) { + this.close(); + reject(new Error('The popup was closed by user')); + return; + } + if (popup.location.origin !== this.redirectOrigin) { + return; + } + resolve(true); + this.close(); + } catch (error) { + /* + * Ignore DOMException: Blocked a frame with origin from accessing a + * cross-origin frame. + */ + } + }, 500); + }); + } + + cancel() { + if (this._iid) { + window.clearInterval(this._iid); + this._iid = null; + } + } + + then(onSuccess: () => void) { + return this.promise?.then(onSuccess); + } + + catch(onError: (err: Error) => void) { + return this.promise?.catch(onError); + } + + static open(url: string, redirectOrigin: string) { + const popup = new OAuth2OidcWindow(url, redirectOrigin); + popup.open(); + popup.poll(); + return popup; + } +} + +export default OAuth2OidcWindow; diff --git a/apps/wallet/src/utils/auth/providers/google.ts b/apps/wallet/src/utils/auth/providers/google.ts index 41a6962..b37b364 100644 --- a/apps/wallet/src/utils/auth/providers/google.ts +++ b/apps/wallet/src/utils/auth/providers/google.ts @@ -1,3 +1,4 @@ +import OAuth2OidcWindow from "../oauth2-oidc-window"; import { IAuthenticateProvider } from "../types"; import { AuthenticatorType } from "@delandlabs/hibit-id-sdk"; @@ -7,8 +8,17 @@ export class GoogleAuthenticateProvider implements IAuthenticateProvider { public readonly type = AuthenticatorType.Google public authenticate: (launchParams?: any) => Promise = async (launchParams?: string) => { - window.location.href = - `${AUTH_SERVER_URL}id/login/google?returnUrl=${encodeURIComponent(`${location.origin}/oidc-login`)}` - return + const loginUrl = `${AUTH_SERVER_URL}id/login/google?returnUrl=${encodeURIComponent(`${location.origin}/oidc-login`)}` + await new Promise((resolve, reject) => { + OAuth2OidcWindow + .open(loginUrl, location.origin) + .then(() => { + location.href = `${location.origin}/oidc-login` + resolve(true) + }) + ?.catch((reason) => { + reject(reason) + }) + }) } } diff --git a/apps/wallet/src/utils/auth/providers/x.ts b/apps/wallet/src/utils/auth/providers/x.ts index b8b45cf..e7a4607 100644 --- a/apps/wallet/src/utils/auth/providers/x.ts +++ b/apps/wallet/src/utils/auth/providers/x.ts @@ -1,3 +1,4 @@ +import OAuth2OidcWindow from "../oauth2-oidc-window"; import { IAuthenticateProvider } from "../types"; import { AuthenticatorType } from "@delandlabs/hibit-id-sdk"; @@ -7,8 +8,17 @@ export class XAuthenticateProvider implements IAuthenticateProvider { public readonly type = AuthenticatorType.X public authenticate: (launchParams?: any) => Promise = async (launchParams?: string) => { - window.location.href = - `${AUTH_SERVER_URL}id/login/twitter?returnUrl=${encodeURIComponent(`${location.origin}/oidc-login`)}` - return + const loginUrl = `${AUTH_SERVER_URL}id/login/twitter?returnUrl=${encodeURIComponent(`${location.origin}/oidc-login`)}` + await new Promise((resolve, reject) => { + OAuth2OidcWindow + .open(loginUrl, location.origin) + .then(() => { + location.href = `${location.origin}/oidc-login` + resolve(true) + }) + ?.catch((reason) => { + reject(reason) + }) + }) } }