0%

electron_issues

停止和退出

app.quit未完全结束进程

在exe执行中未现问题

Cannot find module ‘./src/….’
Github Issue
将文件pattern添加到electron-builder的打包规则中

另外,require import的路径均不区分大小写,configure.ts被活生生编译并打包为Configure.js以及Configure.map.js
import编译成require,找不到configure

无边透明窗体显示时出现闪烁 Issue#10069

该问题是由于webview plugin无法设置透明背景造成的,在BrowserWindow show过程中显示了webview的白色背景,目前的workaround可以延迟页面内容的显示

1
2
3
4
5
6
7
function showBrowserWindow() {
win.setOpacity(0);
win.show();
setTimeout(() => {
win.setOpacity(1);
}, 50);
}

rebuild fail

1
2
gyp ERR! clean error
gyp ERR! stack Error: EPERM: operation not permitted, unlink 'D:\projxxx\node_modules\ref\build\Release\binding.node'

往往是项目文件正在使用中(正在参与其他进程的编译或执行)

oauth2

早先本机应用程序使用嵌入的用户代理(嵌入的web view)进行OAuth授权请求,这种方法有很多缺点,包括主机应用程序
能够复制用户凭据和Cookie,以及需要在每个应用程序中从头进行身份验证的用户。IETF RFC 8252
使用浏览器被认为更加安全且容易保留认证状态

2026-07 修订:这条仍然成立。本机应用包括 Electron,应优先使用系统浏览器 + Authorization Code + PKCE,而不是在 BrowserWindow / webview 中嵌入登录页。旧示例中的 response_type=id_token token 属于 implicit/hybrid 风格,今天不建议作为新实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| User Device |
| |
| +--------------------------+ | (5) Authorization +---------------+
| | | | Code | |
| | Client App |---------------------->| Token |
| | |<----------------------| Endpoint |
| +--------------------------+ | (6) Access Token, | |
| | ^ | Refresh Token +---------------+
| | | |
| | | |
| | (1) | (4) |
| | Authorizat- | Authoriza- |
| | ion Request | tion Code |
| | | |
| | | |
| v | |
| +---------------------------+ | (2) Authorization +---------------+
| | | | Request | |
| | Browser |--------------------->| Authorization |
| | |<---------------------| Endpoint |
| +---------------------------+ | (3) Authorization | |
| | Code +---------------+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+


对于 electron,渲染界面提供入口 signin,点击后调用默认浏览器打开登录页,authenticate 通过后,重定向过程会将授权码返回到 electron。这个“返回”过程可以使用自定义协议实现,也可以使用 loopback localhost callback。

重定向过程会将授权码或直接将access token返回到electron

2026-07 修订:新实现应让浏览器只返回 authorization code,Electron 客户端再用 PKCE 的 code_verifier 换 token。不要让 access token 出现在 URL fragment 中,也不要在 renderer 中长期保存 token。

推荐流程:

  1. Electron 主进程生成 code_verifiercode_challengestatenonce
  2. 使用 shell.openExternal(authUrl) 打开系统浏览器。
  3. 通过自定义协议或 localhost callback 收到 codestate
  4. 校验 state,用 code_verifier 请求 token。
  5. token 存储放在系统凭据管理器、加密存储或主进程内存中,renderer 只拿业务所需的最小状态。

renderUI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
signinWithADB2C(){
const adconfig={
clientid:'3c744214-bf78-4f92-8173-49863ae8f24b',
authority:'https://qqstudio.b2clogin.com/qqstudio.onmicrosoft.com/B2C_1_basic_sign_up_and_sign_in',
redirectUri:'http://localhost:9990/index.html',
scopes:'3c744214-bf78-4f92-8173-49863ae8f24b openid'
}
// make up / assemble authority URL
const authorityURL = `${adconfig.authority}/oauth2/v2.0/authorize?client_id=${
adconfig.clientid
}&redirect_uri=${
encodeURI(adconfig.redirectUri)
}&scope=${
encodeURI(adconfig.scopes)
}&response_type=id_token%20token&nonce=defaultNonce&prompt=login`;
// call main process open URL with default browser
// meanwhile launch a http server
this.ipcService.send('openinbroweser', authorityURL);
this.loading = true;
// listen Logged in message
this.ipcService.on('access_token', msg => {
// TODO process user info
});
}

2026-07 修订:上面代码仅作历史记录。新代码应改为 response_type=code,并添加 code_challengecode_challenge_method=S256、随机 statenonce

main 主线程中launch一个http server,负责提供Redirect URI的页面,页面自执行request请求,请求亦由http server响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
....
ipcMain.on('openinbroweser', (event, args) => {
log('info', 'ipcmain event:openinbroweser');
if (args) {
const { shell } = require('electron');
shell.openExternal(args);
} else {
log('error', 'invalid website', args);
}
});

// launch a http server
var static = require('node-static');
var file = new static.Server(`${__dirname}/public`);
http.createServer(function (request, response) {
if(request.url==='/index.html'){
request.addListener('end', function () {
file.serve(request, response)
}).resume();
}else{
const reg = new RegExp("t=([^&]*)");
const res= request.url.match(reg);
const token = res[1];
console.log('t=',token)
win.webContents.send('access_token',token)
response.write("success"); //close default browser
}
response.end(); //end the response
}).listen(9990)

redirect page (public/index.html)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<body>
<p>serve by csportal</p>
<script>
(() => {
var reg = new RegExp("#access_token=([^&]*)");
var res = location.href.match(reg);
var token = unescape(res[1]);
fetch(`http://localhost:3000?t=${token}`)
.then(function (response) {
return response.json();
})
.catch(error => console.error('Error:', error))
.then(response => {console.log('Success:', response);
window.close();
});
})();
</script>
</body>
</html>

Caution! 需考虑到浏览器将token传递给client的过程,都有被第三方恶意应用占用URL Scheme或者localhost端口截取Access Token的风险。在有”显式”授权流程的方式中,浏览器传递授权码,由client凭授权码换取token,同样无法杜绝第三方拦截。
了解使用带有PKCE(Proof Key for Code Exchange)支持的授权码模式

2026-07 新增:Electron OAuth 安全清单

  • state 必须随机且单次使用,callback 必须校验。
  • localhost callback 使用随机端口,并尽量只监听 127.0.0.1
  • 自定义协议要考虑被其他应用抢注的风险。
  • 不在 renderer、localStorage、日志、URL 中暴露 token。
  • 打开外部链接前校验协议和 host,避免被恶意 URL 利用。