(registrationEndpoint: string, clientName: string)
| 541 | |
| 542 | // Register OAuth client dynamically |
| 543 | private async registerClient(registrationEndpoint: string, clientName: string): Promise<{ |
| 544 | client_id: string; |
| 545 | client_secret?: string; |
| 546 | }> { |
| 547 | const redirectUri = this.config.redirectUri || `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`; |
| 548 | |
| 549 | const response = await fetch(registrationEndpoint, { |
| 550 | method: 'POST', |
| 551 | headers: { 'Content-Type': 'application/json' }, |
| 552 | body: JSON.stringify({ |
| 553 | client_name: clientName, |
| 554 | redirect_uris: [redirectUri], |
| 555 | grant_types: ['authorization_code', 'refresh_token'], |
| 556 | response_types: ['code'], |
| 557 | token_endpoint_auth_method: 'none', // Public client |
| 558 | }), |
| 559 | }); |
| 560 | |
| 561 | if (!response.ok) { |
| 562 | const error = await response.text(); |
| 563 | throw new Error(`Failed to register OAuth client: ${error}`); |
| 564 | } |
| 565 | |
| 566 | return response.json() as Promise<{ |
| 567 | client_id: string; |
| 568 | client_secret?: string; |
| 569 | }>; |
| 570 | } |
| 571 | |
| 572 | // Exchange authorization code for tokens |
| 573 | private async exchangeCodeForTokens( |
no outgoing calls
no test coverage detected