From 6c229c5149e3beff05bcfb42ca286d3e9170e54e Mon Sep 17 00:00:00 2001 From: Erez Rokah Date: Thu, 30 Apr 2020 17:46:07 +0300 Subject: [PATCH] fix(git-gateway): wait for identity widget to initialize (#3660) --- .../src/AuthenticationPage.js | 21 +++-- .../src/implementation.ts | 87 +++++++++++++++---- 2 files changed, 81 insertions(+), 27 deletions(-) diff --git a/packages/netlify-cms-backend-git-gateway/src/AuthenticationPage.js b/packages/netlify-cms-backend-git-gateway/src/AuthenticationPage.js index 560dc90f..c35e90bd 100644 --- a/packages/netlify-cms-backend-git-gateway/src/AuthenticationPage.js +++ b/packages/netlify-cms-backend-git-gateway/src/AuthenticationPage.js @@ -125,7 +125,7 @@ export default class GitGatewayAuthenticationPage extends React.Component { this.setState({ ...this.state, [name]: e.target.value }); }; - handleLogin = e => { + handleLogin = async e => { e.preventDefault(); const { email, password } = this.state; @@ -143,17 +143,16 @@ export default class GitGatewayAuthenticationPage extends React.Component { return; } - GitGatewayAuthenticationPage.authClient - .login(this.state.email, this.state.password, true) - .then(user => { - this.props.onLogin(user); - }) - .catch(error => { - this.setState({ - errors: { server: error.description || error.msg || error }, - loggingIn: false, - }); + try { + const client = await GitGatewayAuthenticationPage.authClient(); + const user = await client.login(this.state.email, this.state.password, true); + this.props.onLogin(user); + } catch (error) { + this.setState({ + errors: { server: error.description || error.msg || error }, + loggingIn: false, }); + } }; render() { diff --git a/packages/netlify-cms-backend-git-gateway/src/implementation.ts b/packages/netlify-cms-backend-git-gateway/src/implementation.ts index 80953805..f53bbf5f 100644 --- a/packages/netlify-cms-backend-git-gateway/src/implementation.ts +++ b/packages/netlify-cms-backend-git-gateway/src/implementation.ts @@ -33,9 +33,22 @@ import GitLabAPI from './GitLabAPI'; import AuthenticationPage from './AuthenticationPage'; import { getClient, Client } from './netlify-lfs-client'; +type NetlifyIdentity = { + logout: () => void; + currentUser: () => User; + on: (event: string, args: unknown) => void; + init: () => void; +}; + +type AuthClient = { + logout: () => void; + currentUser: () => unknown; + login?(email: string, password: string, remember?: boolean): Promise; +}; + declare global { interface Window { - netlifyIdentity?: { gotrue: GoTrue; logout: () => void }; + netlifyIdentity?: NetlifyIdentity; } } @@ -69,6 +82,27 @@ function getEndpoint(endpoint: string, netlifySiteURL: string | null) { return endpoint; } +// wait for identity widget to initialize +// force init on timeout +let initPromise = Promise.resolve() as Promise; +if (window.netlifyIdentity) { + let initialized = false; + initPromise = Promise.race([ + new Promise(resolve => { + window.netlifyIdentity?.on('init', () => { + initialized = true; + resolve(); + }); + }), + new Promise(resolve => setTimeout(resolve, 2500)).then(() => { + if (!initialized) { + console.log('Manually initializing identity widget'); + window.netlifyIdentity?.init(); + } + }), + ]); +} + interface NetlifyUser extends Credentials { jwt: () => Promise; email: string; @@ -85,7 +119,8 @@ export default class GitGateway implements Implementation { gatewayUrl: string; netlifyLargeMediaURL: string; backendType: string | null; - authClient: GoTrue; + apiUrl: string; + authClient?: AuthClient; backend: GitHubBackend | GitLabBackend | BitbucketBackend | null; acceptRoles?: string[]; tokenPromise?: () => Promise; @@ -110,7 +145,7 @@ export default class GitGateway implements Implementation { this.transformImages = config.backend.use_large_media_transforms_in_media_library || true; const netlifySiteURL = localStorage.getItem('netlifySiteURL'); - const APIUrl = getEndpoint(config.backend.identity_url || defaults.identity, netlifySiteURL); + this.apiUrl = getEndpoint(config.backend.identity_url || defaults.identity, netlifySiteURL); this.gatewayUrl = getEndpoint(config.backend.gateway_url || defaults.gateway, netlifySiteURL); this.netlifyLargeMediaURL = getEndpoint( config.backend.large_media_url || defaults.largeMedia, @@ -125,18 +160,40 @@ export default class GitGateway implements Implementation { this.backendType = null; } - this.authClient = window.netlifyIdentity - ? window.netlifyIdentity.gotrue - : new GoTrue({ APIUrl }); - AuthenticationPage.authClient = this.authClient; - this.backend = null; + AuthenticationPage.authClient = () => this.getAuthClient(); } isGitBackend() { return true; } + async getAuthClient() { + if (this.authClient) { + return this.authClient; + } + await initPromise; + if (window.netlifyIdentity) { + this.authClient = { + logout: () => window.netlifyIdentity?.logout(), + currentUser: () => window.netlifyIdentity?.currentUser(), + }; + } else { + const goTrue = new GoTrue({ APIUrl: this.apiUrl }); + this.authClient = { + logout: () => { + const user = goTrue.currentUser(); + if (user) { + return user.logout(); + } + }, + currentUser: () => goTrue.currentUser(), + login: goTrue.login.bind(goTrue), + }; + } + return this.authClient; + } + requestFunction = (req: ApiRequest) => this.tokenPromise!() .then( @@ -233,8 +290,9 @@ export default class GitGateway implements Implementation { return { name: userData.name, login: userData.email } as User; }); } - restoreUser() { - const user = this.authClient && this.authClient.currentUser(); + async restoreUser() { + const client = await this.getAuthClient(); + const user = client.currentUser(); if (!user) return Promise.reject(); return this.authenticate(user as Credentials); } @@ -242,12 +300,9 @@ export default class GitGateway implements Implementation { return AuthenticationPage; } - logout() { - if (window.netlifyIdentity) { - return window.netlifyIdentity.logout(); - } - const user = this.authClient.currentUser(); - return user && user.logout(); + async logout() { + const client = await this.getAuthClient(); + return client.logout(); } getToken() { return this.tokenPromise!();