停止和退出
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
7function 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。
推荐流程:
- Electron 主进程生成
code_verifier、code_challenge、state、nonce。 - 使用
shell.openExternal(authUrl)打开系统浏览器。 - 通过自定义协议或 localhost callback 收到
code和state。 - 校验
state,用code_verifier请求 token。 - token 存储放在系统凭据管理器、加密存储或主进程内存中,renderer 只拿业务所需的最小状态。
renderUI1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24signinWithADB2C(){
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_challenge、code_challenge_method=S256、随机state和nonce。
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 利用。