TidGi-Desktop/features/supports/mockOAuthServer.ts
lin onetwo b76fc17794
Chore/upgrade (#646)
* docs: deps

* Update dependencies and type usage for AI features

Upgraded multiple dependencies in package.json and pnpm-lock.yaml, including @ai-sdk, @mui, react, and others for improved compatibility and performance. Changed type usage from CoreMessage to ModelMessage in mockOpenAI.test.ts to align with updated ai package. No functional changes to application logic.

* feat: i18n

* feat: test oauth login and use PKCE

* fix: use ollama-ai-provider-v2

* test: github and mock oauth2 login

* test: gitea login

* Refactor context menu cleanup and error message

Moved context menu cleanup for OAuth window to a single closed event handler in Authentication service. Simplified error message formatting in ContextService for missing keys.

* lint: AI fix

* Add tsx as a dev dependency and update scripts

Replaced usage of 'pnpm dlx tsx' with direct 'tsx' command in development and test scripts for improved reliability. Added 'tsx' to devDependencies in package.json.
2025-10-23 23:42:06 +08:00

87 lines
2.6 KiB
TypeScript

/**
* Mock OAuth Server using oauth2-mock-server
*
* Replaced custom implementation with professional OAuth 2 mock server.
* Key benefits:
* - Standards-compliant OAuth 2.0 + PKCE
* - Automatic JWT token generation
* - Proper error handling
* - Less code to maintain (from 400+ lines to ~100 lines)
*
* Note: oauth2-mock-server automatically handles authorization.
* It doesn't provide a login UI - it immediately redirects with a code.
* This is perfect for testing token exchange logic without UI complexity.
*
* Standard OAuth 2 endpoints:
* - /authorize (authorization endpoint)
* - /token (token exchange endpoint)
* - /userinfo (user info endpoint)
*/
import { OAuth2Server } from 'oauth2-mock-server';
import type { MutableResponse, MutableToken } from 'oauth2-mock-server';
export class MockOAuthServer {
private server: OAuth2Server | null = null;
public port = 0;
public baseUrl = '';
constructor(
private config: { clientId: string },
private fixedPort?: number,
) {}
async start(): Promise<void> {
this.server = new OAuth2Server();
// Generate RSA key for signing JWT tokens
await this.server.issuer.keys.generate('RS256');
// Start server on specified or random port
await this.server.start(this.fixedPort || 0, '127.0.0.1');
const address = this.server.address();
if (!address || typeof address === 'string') {
throw new Error('Failed to get server address');
}
this.port = address.port;
this.baseUrl = `http://127.0.0.1:${this.port}`;
// Configure issuer URL
this.server.issuer.url = this.baseUrl;
// Setup custom behavior to match real OAuth servers
this.setupCustomBehavior();
}
async stop(): Promise<void> {
if (this.server) {
await this.server.stop();
this.server = null;
}
}
/**
* Customize server behavior to match GitHub/GitLab/Gitea OAuth servers
*/
private setupCustomBehavior(): void {
if (!this.server) return;
// Customize access token to include GitHub-like claims
this.server.service.on('beforeTokenSigning', (token: MutableToken, _request) => {
token.payload.scope = 'user:email,read:user,repo,workflow';
token.payload.token_type = 'bearer';
});
// Simulate user info endpoint (matches GitHub API response)
this.server.service.on('beforeUserinfo', (userInfoResponse: MutableResponse, _request) => {
userInfoResponse.body = {
login: 'testuser',
id: 12345,
email: 'testuser@example.com',
name: 'Test User',
avatar_url: 'https://example.com/avatar.jpg',
};
});
}
}