diff options
Diffstat (limited to 'src/client/apis')
-rw-r--r-- | src/client/apis/GoogleAuthenticationManager.tsx | 84 |
1 files changed, 46 insertions, 38 deletions
diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx index 94ce42d8d..67a6e01e9 100644 --- a/src/client/apis/GoogleAuthenticationManager.tsx +++ b/src/client/apis/GoogleAuthenticationManager.tsx @@ -1,4 +1,4 @@ -import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, IReactionDisposer, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Opt } from '../../fields/Doc'; @@ -6,12 +6,12 @@ import { Networking } from '../Network'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { MainViewModal } from '../views/MainViewModal'; import './GoogleAuthenticationManager.scss'; +import { ObservableReactComponent } from '../views/ObservableReactComponent'; -const AuthenticationUrl = 'https://accounts.google.com/o/oauth2/v2/auth'; const prompt = 'Paste authorization code here...'; @observer -export class GoogleAuthenticationManager extends React.Component<object> { +export class GoogleAuthenticationManager extends ObservableReactComponent<object> { // eslint-disable-next-line no-use-before-define public static Instance: GoogleAuthenticationManager; private authenticationLink: Opt<string> = undefined; @@ -23,6 +23,12 @@ export class GoogleAuthenticationManager extends React.Component<object> { @observable private credentials: { user_info: { name: string; picture: string }; access_token: string } | undefined = undefined; private disposer: Opt<IReactionDisposer>; + constructor(props: object) { + super(props); + makeObservable(this); + GoogleAuthenticationManager.Instance = this; + } + private set isOpen(value: boolean) { runInAction(() => (this.openState = value)); } @@ -35,44 +41,51 @@ export class GoogleAuthenticationManager extends React.Component<object> { this.openState && this.resetState(0, 0); } - public fetchOrGenerateAccessToken = async (displayIfFound = false) => { + public fetchOrGenerateAccessToken = async (): Promise<string | undefined> => { const response = await Networking.FetchFromServer('/readGoogleAccessToken'); - // if this is an authentication url, activate the UI to register the new access token - if (new RegExp(AuthenticationUrl).test(response)) { - this.isOpen = true; - this.authenticationLink = response; - return new Promise<string>(resolve => { - this.disposer?.(); - this.disposer = reaction( - () => this.authenticationCode, - async authenticationCode => { - if (authenticationCode && /\d{1}\/[\w-]{55}/.test(authenticationCode)) { - this.disposer?.(); - const response2 = await Networking.PostToServer('/writeGoogleAccessToken', { authenticationCode }); - runInAction(() => { - this.success = true; - this.credentials = response2 as { user_info: { name: string; picture: string }; access_token: string }; - }); - this.resetState(); - resolve((response2 as { access_token: string }).access_token); - } - } - ); + + // This will return a JSON object with { access_token, user_info } if already linked + try { + const parsed = JSON.parse(response) as { access_token: string; user_info: { name: string; picture: string } }; + + runInAction(() => { + this.success = true; + this.credentials = parsed; }); + + return parsed.access_token; + } catch { + console.warn('Not linked yet or invalid JSON. open auth...'); + // This is an auth URL — redirect the user to /refreshGoogle + if (typeof response === 'string' && response.startsWith('http')) { + if (window.confirm('Authorize Dash to access your Google account?')) { + window.open(response)?.focus(); + return undefined; + } + } + + throw new Error('Unable to fetch Google access token.'); } + }; - // otherwise, we already have a valid, stored access token and user info - const response2 = JSON.parse(response) as { user_info: { name: string; picture: string }; access_token: string }; - if (displayIfFound) { + public fetchAccessTokenSilently = async (): Promise<string | undefined> => { + const response = await Networking.FetchFromServer('/readGoogleAccessToken'); + + try { + const parsed = JSON.parse(response) as { access_token: string; user_info: { name: string; picture: string } }; + runInAction(() => { this.success = true; - this.credentials = response2; + this.credentials = parsed; }); - this.resetState(-1, -1); - this.isOpen = true; + + return parsed.access_token; + } catch { + // Do nothing — just return undefined silently + return undefined; } - return (response2 as { access_token: string }).access_token; }; + resetState = action((visibleForMS: number = 3000, fadesOutInMS: number = 500) => { if (!visibleForMS && !fadesOutInMS) { @@ -107,11 +120,6 @@ export class GoogleAuthenticationManager extends React.Component<object> { } }); - constructor(props: object) { - super(props); - GoogleAuthenticationManager.Instance = this; - } - private get renderPrompt() { return ( <div className={'authorize-container'}> @@ -127,7 +135,7 @@ export class GoogleAuthenticationManager extends React.Component<object> { </button> ) : null} {this.showPasteTargetState ? <input className={'paste-target'} onChange={action(e => (this.authenticationCode = e.currentTarget.value))} placeholder={prompt} /> : null} - {this.credentials ? ( + {this.credentials?.user_info?.picture ? ( <> <img className={'avatar'} src={this.credentials.user_info.picture} /> <span className={'welcome'}>Welcome to Dash, {this.credentials.user_info.name}</span> |