fix: failed to load blobs in electron (#1927)

This commit is contained in:
Peng Xiao 2023-04-13 23:14:46 +08:00 committed by GitHub
parent 934e242116
commit 42756045bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 27 additions and 32 deletions

View File

@ -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 };
};

View File

@ -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<string>((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);
});
});

View File

@ -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 });

View File

@ -7,6 +7,6 @@ interface Window {
*
* @see https://github.com/cawa-93/dts-for-context-bridge
*/
readonly apis: { workspaceSync: (id: string) => Promise<any>; onThemeChange: (theme: string) => Promise<any>; onSidebarVisibilityChange: (visible: boolean) => Promise<any>; googleSignIn: () => Promise<string>; updateEnv: (env: string, value: string) => void; };
readonly apis: { workspaceSync: (id: string) => Promise<any>; onThemeChange: (theme: string) => Promise<any>; onSidebarVisibilityChange: (visible: boolean) => Promise<any>; getGoogleOauthCode: () => Promise<{ requestInit: RequestInit; url: string; }>; updateEnv: (env: string, value: string) => void; };
readonly appInfo: { electron: boolean; isMacOS: boolean; };
}

View File

@ -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<string> => 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

View File

@ -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,

View File

@ -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);
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 = () => {