@@ -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 ( / \/ g u i \/ .* $ / , '/' ) + '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