diff --git a/apps/electron/layers/main/src/app-state/google-auth.ts b/apps/electron/layers/main/src/app-state/google-auth.ts index b00d8d9afa..b26bce420f 100644 --- a/apps/electron/layers/main/src/app-state/google-auth.ts +++ b/apps/electron/layers/main/src/app-state/google-auth.ts @@ -1,16 +1,10 @@ -import type { RequestInit } from 'undici'; -import { fetch, ProxyAgent } from 'undici'; - const redirectUri = 'https://affine.pro/client/auth-callback'; export const oauthEndpoint = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${process.env.AFFINE_GOOGLE_CLIENT_ID}&redirect_uri=${redirectUri}&response_type=code&scope=openid https://www.googleapis.com/auth/userinfo.email profile&access_type=offline&customParameters={"prompt":"select_account"}`; const tokenEndpoint = 'https://oauth2.googleapis.com/token'; -export const exchangeToken = async (code: string) => { - const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy; - const proxyAgent = httpProxy ? new ProxyAgent(httpProxy) : undefined; - +export const getExchangeTokenParams = (code: string) => { const postData = { code, client_id: process.env.AFFINE_GOOGLE_CLIENT_ID || '', @@ -18,15 +12,12 @@ export const exchangeToken = async (code: string) => { redirect_uri: redirectUri, grant_type: 'authorization_code', }; - const requestOptions: RequestInit = { + const requestInit: RequestInit = { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams(postData).toString(), - dispatcher: proxyAgent, }; - return fetch(tokenEndpoint, requestOptions).then(response => { - return response.json(); - }); + return { requestInit, url: tokenEndpoint }; }; diff --git a/apps/electron/layers/main/src/app-state/index.ts b/apps/electron/layers/main/src/app-state/index.ts index f8372a4a04..79f9fb6e0d 100644 --- a/apps/electron/layers/main/src/app-state/index.ts +++ b/apps/electron/layers/main/src/app-state/index.ts @@ -7,7 +7,7 @@ import { BrowserWindow, ipcMain, nativeTheme } from 'electron'; import fs from 'fs-extra'; import { parse } from 'url'; -import { exchangeToken, oauthEndpoint } from './google-auth'; +import { getExchangeTokenParams, oauthEndpoint } from './google-auth'; const AFFINE_ROOT = path.join(os.homedir(), '.affine'); @@ -40,29 +40,24 @@ export const registerHandlers = () => { logger.info('sidebar visibility change', visible); }); - ipcMain.handle('ui:google-sign-in', async () => { + ipcMain.handle('ui:get-google-oauth-code', async () => { logger.info('starting google sign in ...'); shell.openExternal(oauthEndpoint); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const handleOpenUrl = async (_: any, url: string) => { const mainWindow = BrowserWindow.getAllWindows().find( w => !w.isDestroyed() ); const urlObj = parse(url.replace('??', '?'), true); - if (!mainWindow || !url.startsWith('affine://')) return; + if (!mainWindow || !url.startsWith('affine://auth-callback')) return; const code = urlObj.query['code'] as string; if (!code) return; logger.info('google sign in code received from callback', code); - const token = (await exchangeToken(code)) as { - id_token: string; - }; - app.removeListener('open-url', handleOpenUrl); - resolve(token.id_token); - logger.info('google sign in successful', token); + resolve(getExchangeTokenParams(code)); }; app.on('open-url', handleOpenUrl); @@ -70,7 +65,7 @@ export const registerHandlers = () => { setTimeout(() => { reject(new Error('Timed out')); app.removeListener('open-url', handleOpenUrl); - }, 60000); + }, 30000); }); }); diff --git a/apps/electron/layers/main/src/protocol.ts b/apps/electron/layers/main/src/protocol.ts index 2725d715f1..4770be4dc8 100644 --- a/apps/electron/layers/main/src/protocol.ts +++ b/apps/electron/layers/main/src/protocol.ts @@ -36,6 +36,7 @@ export function registerProtocol() { const realpath = toAbsolutePath(url); // console.log('realpath', realpath, 'for', url); callback(realpath); + return true; }); protocol.registerFileProtocol('assets', (request, callback) => { @@ -43,6 +44,7 @@ export function registerProtocol() { const realpath = toAbsolutePath(url); // console.log('realpath', realpath, 'for', url); callback(realpath); + return true; }); } @@ -60,7 +62,6 @@ export function registerProtocol() { 'DELETE', 'OPTIONS', ]; - // responseHeaders['Content-Security-Policy'] = ["default-src 'self'"]; } callback({ responseHeaders }); diff --git a/apps/electron/layers/preload/preload.d.ts b/apps/electron/layers/preload/preload.d.ts index d407bda96b..2493e6d032 100644 --- a/apps/electron/layers/preload/preload.d.ts +++ b/apps/electron/layers/preload/preload.d.ts @@ -7,6 +7,6 @@ interface Window { * * @see https://github.com/cawa-93/dts-for-context-bridge */ - readonly apis: { workspaceSync: (id: string) => Promise; onThemeChange: (theme: string) => Promise; onSidebarVisibilityChange: (visible: boolean) => Promise; googleSignIn: () => Promise; updateEnv: (env: string, value: string) => void; }; + readonly apis: { workspaceSync: (id: string) => Promise; onThemeChange: (theme: string) => Promise; onSidebarVisibilityChange: (visible: boolean) => Promise; getGoogleOauthCode: () => Promise<{ requestInit: RequestInit; url: string; }>; updateEnv: (env: string, value: string) => void; }; readonly appInfo: { electron: boolean; isMacOS: boolean; }; } diff --git a/apps/electron/layers/preload/src/index.ts b/apps/electron/layers/preload/src/index.ts index ab900821a9..01bfe976e5 100644 --- a/apps/electron/layers/preload/src/index.ts +++ b/apps/electron/layers/preload/src/index.ts @@ -31,9 +31,11 @@ contextBridge.exposeInMainWorld('apis', { ipcRenderer.invoke('ui:sidebar-visibility-change', visible), /** - * Try sign in using Google and return a Google IDToken + * Try sign in using Google and return a request object to exchange the code for a token + * Not exchange in Node side because it is easier to do it in the renderer with VPN */ - googleSignIn: (): Promise => ipcRenderer.invoke('ui:google-sign-in'), + getGoogleOauthCode: (): Promise<{ requestInit: RequestInit; url: string }> => + ipcRenderer.invoke('ui:get-google-oauth-code'), /** * Secret backdoor to update environment variables in main process diff --git a/apps/web/src/plugins/affine/index.tsx b/apps/web/src/plugins/affine/index.tsx index 5f661e0b55..f4985e5392 100644 --- a/apps/web/src/plugins/affine/index.tsx +++ b/apps/web/src/plugins/affine/index.tsx @@ -52,7 +52,10 @@ const getPersistenceAllWorkspace = () => { item.id, (k: string) => // fixme: token could be expired - ({ api: '/api/workspace', token: getLoginStorage()?.token }[k]) + ({ + api: prefixUrl + 'api/workspace', + token: getLoginStorage()?.token, + }[k]) ); const affineWorkspace: AffineWorkspace = { ...item, diff --git a/packages/workspace/src/affine/login.ts b/packages/workspace/src/affine/login.ts index 7de68c8e37..0484bc5eb0 100644 --- a/packages/workspace/src/affine/login.ts +++ b/packages/workspace/src/affine/login.ts @@ -67,10 +67,13 @@ export const setLoginStorage = (login: LoginResponse) => { }; const signInWithElectron = async (firebaseAuth: FirebaseAuth) => { - const code = await window.apis?.googleSignIn(); - const credential = GoogleAuthProvider.credential(code); - const user = await signInWithCredential(firebaseAuth, credential); - return await user.user.getIdToken(); + if (window.apis) { + const { url, requestInit } = await window.apis.getGoogleOauthCode(); + const { id_token } = await fetch(url, requestInit).then(res => res.json()); + const credential = GoogleAuthProvider.credential(id_token); + const user = await signInWithCredential(firebaseAuth, credential); + return await user.user.getIdToken(); + } }; export const clearLoginStorage = () => {