Skip to content

Commit 60b0366

Browse files
committed
Refactor ChatGPT authentication into a dedicated utility module and update imports
- move [Fragment] to a separate util
1 parent 8cbb6e4 commit 60b0366

3 files changed

Lines changed: 106 additions & 103 deletions

File tree

‎apps/editor/src/views/settings/backend/message-handlers/handle-edit-model-provider-key.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
import { dictionary } from '@shared/constants/dictionary'
88
import { ChangeModelProviderKeyMessage } from '@/views/settings/types/messages'
99
import { handle_get_model_providers } from './handle-get-model-providers'
10-
import { authenticate_chatgpt } from '../../../utils/upsert-model-provider'
10+
import { authenticate_chatgpt } from '../../../utils/authenticate-chatgpt'
1111

1212
export const handle_change_model_provider_key = async (
1313
provider: SettingsProvider,
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import * as vscode from 'vscode'
2+
import axios from 'axios'
3+
import * as crypto from 'crypto'
4+
import * as http from 'http'
5+
import { URL, URLSearchParams } from 'url'
6+
7+
export const authenticate_chatgpt = async (): Promise<string> => {
8+
const client_id = 'app_EMoamEEZ73f0CkXaXp7hrann'
9+
const redirect_uri = 'http://localhost:1455/auth/callback'
10+
const auth_endpoint = 'https://auth.openai.com/oauth/authorize'
11+
const token_endpoint = 'https://auth.openai.com/oauth/token'
12+
13+
const code_verifier = crypto.randomBytes(32).toString('base64url')
14+
const code_challenge = crypto
15+
.createHash('sha256')
16+
.update(code_verifier)
17+
.digest('base64url')
18+
const state = crypto.randomBytes(16).toString('hex')
19+
20+
const params = new URLSearchParams({
21+
client_id,
22+
redirect_uri,
23+
scope: 'openid profile email offline_access',
24+
code_challenge,
25+
code_challenge_method: 'S256',
26+
response_type: 'code',
27+
state,
28+
codex_cli_simplified_flow: 'true',
29+
originator: 'code-web-chat'
30+
})
31+
32+
const auth_url = `${auth_endpoint}?${params.toString()}`
33+
await vscode.env.openExternal(vscode.Uri.parse(auth_url))
34+
35+
return new Promise((resolve, reject) => {
36+
const server = http.createServer(async (req, res) => {
37+
try {
38+
const url = new URL(req.url || '', 'http://localhost:1455')
39+
if (url.pathname != '/auth/callback') {
40+
res.writeHead(404)
41+
res.end('Not Found')
42+
return
43+
}
44+
45+
const code = url.searchParams.get('code')
46+
const req_state = url.searchParams.get('state')
47+
const error = url.searchParams.get('error')
48+
49+
if (error) {
50+
res.writeHead(400)
51+
res.end(`Authentication failed: ${error}`)
52+
server.close()
53+
return reject(new Error(error))
54+
}
55+
56+
if (!code || req_state !== state) {
57+
res.writeHead(400)
58+
res.end('Invalid request')
59+
server.close()
60+
return reject(new Error('Invalid request'))
61+
}
62+
63+
const body = new URLSearchParams({
64+
grant_type: 'authorization_code',
65+
client_id,
66+
code,
67+
redirect_uri,
68+
code_verifier
69+
})
70+
71+
const token_res = await axios.post(token_endpoint, body.toString(), {
72+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
73+
})
74+
75+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
76+
res.end(
77+
`<!DOCTYPE html><html><body><h1>Authentication Successful</h1><p>You can close this window and return to VS Code.</p><script>setTimeout(() => window.close(), 3000);</script></body></html>`
78+
)
79+
80+
server.close()
81+
resolve(token_res.data.access_token)
82+
} catch (err) {
83+
res.writeHead(500)
84+
res.end('Internal Server Error')
85+
server.close()
86+
reject(err)
87+
}
88+
})
89+
90+
server.on('error', (err) => {
91+
reject(err)
92+
})
93+
94+
setTimeout(
95+
() => {
96+
server.close()
97+
reject(new Error('Authentication timed out'))
98+
},
99+
5 * 60 * 1000
100+
)
101+
102+
server.listen(1455)
103+
})
104+
}

‎apps/editor/src/views/utils/upsert-model-provider.ts‎

Lines changed: 1 addition & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import * as vscode from 'vscode'
22
import axios from 'axios'
3-
import * as crypto from 'crypto'
4-
import * as http from 'http'
5-
import { URL } from 'url'
63
import {
74
CustomProvider,
85
ModelProvidersManager,
96
Provider
107
} from '@/services/model-providers-manager'
118
import { dictionary } from '@shared/constants/dictionary'
129
import { PROVIDERS } from '@shared/constants/providers'
10+
import { authenticate_chatgpt } from './authenticate-chatgpt'
1311

1412
const normalize_base_url = (url: string): string => {
1513
return url.trim().replace(/\/+$/, '')
@@ -566,102 +564,3 @@ export const upsert_model_provider = async (params: {
566564
await providers_manager.save_providers(updated_providers)
567565
return working_provider
568566
}
569-
570-
export const authenticate_chatgpt = async (): Promise<string> => {
571-
const client_id = 'app_EMoamEEZ73f0CkXaXp7hrann'
572-
const redirect_uri = 'http://localhost:1455/auth/callback'
573-
const auth_endpoint = 'https://auth.openai.com/oauth/authorize'
574-
const token_endpoint = 'https://auth.openai.com/oauth/token'
575-
576-
const code_verifier = crypto.randomBytes(32).toString('base64url')
577-
const code_challenge = crypto
578-
.createHash('sha256')
579-
.update(code_verifier)
580-
.digest('base64url')
581-
const state = crypto.randomBytes(16).toString('hex')
582-
583-
const params = new URLSearchParams({
584-
client_id,
585-
redirect_uri,
586-
scope: 'openid profile email offline_access',
587-
code_challenge,
588-
code_challenge_method: 'S256',
589-
response_type: 'code',
590-
state,
591-
codex_cli_simplified_flow: 'true',
592-
originator: 'code-web-chat'
593-
})
594-
595-
const auth_url = `${auth_endpoint}?${params.toString()}`
596-
await vscode.env.openExternal(vscode.Uri.parse(auth_url))
597-
598-
return new Promise((resolve, reject) => {
599-
const server = http.createServer(async (req, res) => {
600-
try {
601-
const url = new URL(req.url || '', 'http://localhost:1455')
602-
if (url.pathname != '/auth/callback') {
603-
res.writeHead(404)
604-
res.end('Not Found')
605-
return
606-
}
607-
608-
const code = url.searchParams.get('code')
609-
const req_state = url.searchParams.get('state')
610-
const error = url.searchParams.get('error')
611-
612-
if (error) {
613-
res.writeHead(400)
614-
res.end(`Authentication failed: ${error}`)
615-
server.close()
616-
return reject(new Error(error))
617-
}
618-
619-
if (!code || req_state !== state) {
620-
res.writeHead(400)
621-
res.end('Invalid request')
622-
server.close()
623-
return reject(new Error('Invalid request'))
624-
}
625-
626-
const body = new URLSearchParams({
627-
grant_type: 'authorization_code',
628-
client_id,
629-
code,
630-
redirect_uri,
631-
code_verifier
632-
})
633-
634-
const token_res = await axios.post(token_endpoint, body.toString(), {
635-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
636-
})
637-
638-
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
639-
res.end(
640-
`<!DOCTYPE html><html><body><h1>Authentication Successful</h1><p>You can close this window and return to VS Code.</p><script>setTimeout(() => window.close(), 3000);</script></body></html>`
641-
)
642-
643-
server.close()
644-
resolve(token_res.data.access_token)
645-
} catch (err) {
646-
res.writeHead(500)
647-
res.end('Internal Server Error')
648-
server.close()
649-
reject(err)
650-
}
651-
})
652-
653-
server.on('error', (err) => {
654-
reject(err)
655-
})
656-
657-
setTimeout(
658-
() => {
659-
server.close()
660-
reject(new Error('Authentication timed out'))
661-
},
662-
5 * 60 * 1000
663-
)
664-
665-
server.listen(1455)
666-
})
667-
}

0 commit comments

Comments
 (0)