LiuVaayne 35c50b54a8
Feat/mcp oauth (#4837)
* feat: implement OAuth client provider and lockfile management

* feat: implement OAuth callback server and refactor authentication flow

* fix(McpService): restrict command handling to 'npx' for improved clarity

* refactor: make callbackPort optional in OAuthProviderOptions and clean up MCPService

* refactor: restructure OAuth handling by creating separate callback and provider classes, and remove unused utility functions
2025-04-16 22:07:32 +08:00

77 lines
2.1 KiB
TypeScript

import Logger from 'electron-log'
import EventEmitter from 'events'
import http from 'http'
import { URL } from 'url'
import { OAuthCallbackServerOptions } from './types'
export class CallBackServer {
private server: Promise<http.Server>
private events: EventEmitter
constructor(options: OAuthCallbackServerOptions) {
const { port, path, events } = options
this.events = events
this.server = this.initialize(port, path)
}
initialize(port: number, path: string): Promise<http.Server> {
const server = http.createServer((req, res) => {
// Only handle requests to the callback path
if (req.url?.startsWith(path)) {
try {
// Parse the URL to extract the authorization code
const url = new URL(req.url, `http://localhost:${port}`)
const code = url.searchParams.get('code')
if (code) {
// Emit the code event
this.events.emit('auth-code-received', code)
}
} catch (error) {
Logger.error('Error processing OAuth callback:', error)
res.writeHead(500, { 'Content-Type': 'text/plain' })
res.end('Internal Server Error')
}
} else {
// Not a callback request
res.writeHead(404, { 'Content-Type': 'text/plain' })
res.end('Not Found')
}
})
// Handle server errors
server.on('error', (error) => {
Logger.error('OAuth callback server error:', error)
})
const runningServer = new Promise<http.Server>((resolve, reject) => {
server.listen(port, () => {
Logger.info(`OAuth callback server listening on port ${port}`)
resolve(server)
})
server.on('error', (error) => {
reject(error)
})
})
return runningServer
}
get getServer(): Promise<http.Server> {
return this.server
}
async close() {
const server = await this.server
server.close()
}
async waitForAuthCode(): Promise<string> {
return new Promise((resolve) => {
this.events.once('auth-code-received', (code) => {
resolve(code)
})
})
}
}