Skip to content

Commit e2a9fa6

Browse files
author
Deep-sea-school
committed
加入对桌面端的支持,我在赌
1 parent 763cb21 commit e2a9fa6

2 files changed

Lines changed: 98 additions & 25 deletions

File tree

src/components/github-oauth-modal/github-oauth-modal.jsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,22 @@ const GitHubOAuthModal = props => {
190190
setIsAuthenticating(true);
191191
setError('');
192192

193-
await githubOAuth.startOAuth(CLIENT_ID);
194-
// 如果 startOAuth 成功,页面会重定向到 GitHub
193+
// 检查是否在 Electron 环境中
194+
const isElectron = typeof window.EditorPreload !== 'undefined';
195+
196+
if (isElectron) {
197+
// 在 Electron 环境中,使用轮询的方式处理 OAuth
198+
const result = await githubOAuth.startOAuth(CLIENT_ID);
199+
setUserInfo({
200+
...result.user,
201+
email: result.email
202+
});
203+
204+
onSuccess && onSuccess(result);
205+
} else {
206+
await githubOAuth.startOAuth(CLIENT_ID);
207+
// 如果 startOAuth 成功,页面会重定向到 GitHub
208+
}
195209
} catch (err) {
196210
console.error('OAuth start failed:', err);
197211
setError(err.message);

src/lib/github-oauth.js

Lines changed: 82 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class GitHubOAuthService {
1212
this.userStorageKey = 'github_user';
1313
this.emailStorageKey = 'github_email';
1414
this.clientIdStorageKey = 'github_oauth_client_id';
15+
this.isElectron = typeof window.EditorPreload !== 'undefined';
1516
}
1617

1718
/**
@@ -50,42 +51,100 @@ class GitHubOAuthService {
5051
/**
5152
* 启动 OAuth 认证流程
5253
* @param {string} clientId - GitHub OAuth App Client ID
53-
* @returns {Promise<void>}
54+
* @returns {Promise<any>} - 在 Electron 环境中返回认证结果,否则返回 undefined
5455
*/
5556
async startOAuth(clientId) {
5657
if (!clientId) {
5758
throw new Error('Client ID is required');
5859
}
5960

6061
try {
61-
// 生成 PKCE 挑战码
62-
const codeVerifier = this.generateRandomString(128);
63-
const codeChallenge = await this.sha256(codeVerifier);
64-
65-
// 保存到 sessionStorage
66-
sessionStorage.setItem('code_verifier', codeVerifier);
67-
sessionStorage.setItem('client_id', clientId);
68-
69-
// 构建授权 URL
70-
const authUrl = new URL('https://github.com/login/oauth/authorize');
71-
authUrl.searchParams.append('client_id', clientId);
72-
authUrl.searchParams.append('redirect_uri', this.redirectUri);
73-
authUrl.searchParams.append('scope', 'repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,gist,notifications,user,delete_repo,write:packages,read:packages,delete:packages,admin:gpg_key,workflow');
74-
authUrl.searchParams.append('code_challenge', codeChallenge);
75-
authUrl.searchParams.append('code_challenge_method', 'S256');
76-
authUrl.searchParams.append('state', this.generateRandomString(32));
77-
78-
// 保存 Client ID
79-
localStorage.setItem(this.clientIdStorageKey, clientId);
80-
81-
// 重定向到 GitHub
82-
window.location.href = authUrl.toString();
62+
// 检查是否在 Electron 环境中
63+
if (this.isElectron) {
64+
// 在 Electron 中,打开 oauth-proxy 页面而不是直接重定向到 GitHub
65+
const proxyUrl = window.location.origin + window.location.pathname.replace(/\/gui\/.*$/, '/') + 'oauth-proxy.html';
66+
window.open(proxyUrl, '_blank', 'width=600,height=800');
67+
68+
// 启动轮询检查是否收到 token,并返回结果
69+
return await this.pollForToken();
70+
} else {
71+
// 在浏览器环境中,生成 PKCE 挑战码
72+
const codeVerifier = this.generateRandomString(128);
73+
const codeChallenge = await this.sha256(codeVerifier);
74+
75+
// 保存到 sessionStorage
76+
sessionStorage.setItem('code_verifier', codeVerifier);
77+
sessionStorage.setItem('client_id', clientId);
78+
79+
// 构建授权 URL
80+
const authUrl = new URL('https://github.com/login/oauth/authorize');
81+
authUrl.searchParams.append('client_id', clientId);
82+
authUrl.searchParams.append('redirect_uri', this.redirectUri);
83+
authUrl.searchParams.append('scope', 'repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,gist,notifications,user,delete_repo,write:packages,read:packages,delete:packages,admin:gpg_key,workflow');
84+
authUrl.searchParams.append('code_challenge', codeChallenge);
85+
authUrl.searchParams.append('code_challenge_method', 'S256');
86+
authUrl.searchParams.append('state', this.generateRandomString(32));
87+
88+
// 保存 Client ID
89+
localStorage.setItem(this.clientIdStorageKey, clientId);
90+
91+
// 重定向到 GitHub
92+
window.location.href = authUrl.toString();
93+
}
8394
} catch (error) {
8495
console.error('OAuth start failed:', error);
8596
throw error;
8697
}
8798
}
8899

100+
/**
101+
* 轮询检查是否在 localStorage 中接收到 token
102+
* @returns {Promise<Object>} 用户信息
103+
*/
104+
async pollForToken() {
105+
return new Promise((resolve, reject) => {
106+
const maxAttempts = 60; // 最多等待 30 秒 (60 次 * 500ms)
107+
let attempts = 0;
108+
109+
const poll = async () => {
110+
attempts++;
111+
112+
if (attempts > maxAttempts) {
113+
reject(new Error('OAuth timeout: No token received within 30 seconds'));
114+
return;
115+
}
116+
117+
try {
118+
// 检查是否在 localStorage 中存在 OAuth 相关信息
119+
const token = localStorage.getItem('oauth_token_received');
120+
const user = localStorage.getItem('github_user');
121+
const email = localStorage.getItem('github_email');
122+
123+
if (token && user && email) {
124+
// 清除临时存储
125+
localStorage.removeItem('oauth_token_received');
126+
127+
// 保存到正常的存储位置
128+
localStorage.setItem(this.tokenStorageKey, token);
129+
localStorage.setItem(this.userStorageKey, user);
130+
localStorage.setItem(this.emailStorageKey, email);
131+
132+
// 返回用户信息
133+
const userInfo = JSON.parse(user);
134+
resolve({ user: userInfo, email, token });
135+
return;
136+
}
137+
} catch (error) {
138+
console.error('Error checking for token:', error);
139+
}
140+
141+
setTimeout(poll, 500); // 每 500ms 检查一次
142+
};
143+
144+
poll();
145+
});
146+
}
147+
89148
/**
90149
* 处理 OAuth 回调
91150
* @returns {Promise<Object>} 用户信息

0 commit comments

Comments
 (0)