From 755dc70d1bf598af5120eebac68f76618da70134 Mon Sep 17 00:00:00 2001 From: simonredfern Date: Sun, 28 Dec 2025 15:28:42 +0100 Subject: [PATCH] Fix TypeScript compilation errors in multi-provider implementation - Fix OAuth2ClientWithConfig to properly extend arctic OAuth2Client - Rename methods to avoid base class conflicts (exchangeAuthorizationCode, refreshTokens) - Fix OAuth2ProviderManager to use OBPClientService.get() correctly - Fix iteration over Map entries to avoid downlevelIteration issues - Update OAuth2ConnectController with correct method signatures - Fix redirect URI access via getRedirectUri() method --- .../controllers/OAuth2CallbackController.ts | 2 +- server/controllers/OAuth2ConnectController.ts | 6 +-- server/services/OAuth2ClientWithConfig.ts | 43 +++++++++++++------ server/services/OAuth2ProviderManager.ts | 17 +++----- 4 files changed, 40 insertions(+), 28 deletions(-) diff --git a/server/controllers/OAuth2CallbackController.ts b/server/controllers/OAuth2CallbackController.ts index d6b7ac4..021faa3 100644 --- a/server/controllers/OAuth2CallbackController.ts +++ b/server/controllers/OAuth2CallbackController.ts @@ -205,7 +205,7 @@ export class OAuth2CallbackController { // Exchange code for tokens console.log(`OAuth2CallbackController: Exchanging authorization code for tokens`) - const tokens = await client.validateAuthorizationCode(code, codeVerifier) + const tokens = await client.exchangeAuthorizationCode(code, codeVerifier) // Store tokens in session session.oauth2_access_token = tokens.accessToken diff --git a/server/controllers/OAuth2ConnectController.ts b/server/controllers/OAuth2ConnectController.ts index 50d95fe..183e018 100644 --- a/server/controllers/OAuth2ConnectController.ts +++ b/server/controllers/OAuth2ConnectController.ts @@ -169,14 +169,14 @@ export class OAuth2ConnectController { session.oauth2_state = state // Use legacy service to create authorization URL - const authUrl = this.legacyOAuth2Service.createAuthorizationURL(state, codeVerifier, [ + const authUrl = this.legacyOAuth2Service.createAuthorizationURL(state, [ 'openid', 'profile', 'email' ]) console.log('OAuth2ConnectController: Redirecting to legacy OIDC provider') - return response.redirect(authUrl) + return response.redirect(authUrl.toString()) } /** @@ -186,7 +186,7 @@ export class OAuth2ConnectController { const authEndpoint = client.getAuthorizationEndpoint() const params = new URLSearchParams({ client_id: client.clientId, - redirect_uri: client.redirectURI, + redirect_uri: client.getRedirectUri(), response_type: 'code', scope: 'openid profile email', state: state, diff --git a/server/services/OAuth2ClientWithConfig.ts b/server/services/OAuth2ClientWithConfig.ts index 21fdbae..670bc08 100644 --- a/server/services/OAuth2ClientWithConfig.ts +++ b/server/services/OAuth2ClientWithConfig.ts @@ -25,7 +25,7 @@ * */ -import { OAuth2Client } from 'arctic' +import { OAuth2Client, OAuth2Tokens } from 'arctic' import type { OIDCConfiguration, TokenResponse } from '../types/oauth2.js' /** @@ -48,10 +48,14 @@ import type { OIDCConfiguration, TokenResponse } from '../types/oauth2.js' export class OAuth2ClientWithConfig extends OAuth2Client { public OIDCConfig?: OIDCConfiguration public provider: string + private _clientSecret: string + private _redirectUri: string constructor(clientId: string, clientSecret: string, redirectUri: string, provider: string) { super(clientId, clientSecret, redirectUri) this.provider = provider + this._clientSecret = clientSecret + this._redirectUri = redirectUri } /** @@ -158,16 +162,15 @@ export class OAuth2ClientWithConfig extends OAuth2Client { } /** - * Validate authorization code and exchange for tokens + * Exchange authorization code for tokens * - * This method extends the base OAuth2Client functionality to support - * provider-specific token exchange requirements (e.g., Basic Auth vs form-based credentials) + * This method provides a simpler interface for token exchange * * @param code - Authorization code from OIDC provider * @param codeVerifier - PKCE code verifier * @returns Token response with access token, refresh token, and ID token */ - async validateAuthorizationCode(code: string, codeVerifier: string): Promise { + async exchangeAuthorizationCode(code: string, codeVerifier: string): Promise { const tokenEndpoint = this.getTokenEndpoint() console.log(`OAuth2ClientWithConfig: Exchanging authorization code for ${this.provider}`) @@ -176,19 +179,19 @@ export class OAuth2ClientWithConfig extends OAuth2Client { const body = new URLSearchParams({ grant_type: 'authorization_code', code: code, - redirect_uri: this.redirectURI, + redirect_uri: this._redirectUri, code_verifier: codeVerifier, client_id: this.clientId }) // Add client_secret to body (some providers prefer this over Basic Auth) - if (this.clientSecret) { - body.append('client_secret', this.clientSecret) + if (this._clientSecret) { + body.append('client_secret', this._clientSecret) } try { // Try with Basic Authentication first (RFC 6749 standard) - const authHeader = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64') + const authHeader = Buffer.from(`${this.clientId}:${this._clientSecret}`).toString('base64') const response = await fetch(tokenEndpoint, { method: 'POST', @@ -229,7 +232,7 @@ export class OAuth2ClientWithConfig extends OAuth2Client { * @param refreshToken - Refresh token from previous authentication * @returns New token response */ - async refreshAccessToken(refreshToken: string): Promise { + async refreshTokens(refreshToken: string): Promise { const tokenEndpoint = this.getTokenEndpoint() console.log(`OAuth2ClientWithConfig: Refreshing access token for ${this.provider}`) @@ -240,12 +243,12 @@ export class OAuth2ClientWithConfig extends OAuth2Client { client_id: this.clientId }) - if (this.clientSecret) { - body.append('client_secret', this.clientSecret) + if (this._clientSecret) { + body.append('client_secret', this._clientSecret) } try { - const authHeader = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64') + const authHeader = Buffer.from(`${this.clientId}:${this._clientSecret}`).toString('base64') const response = await fetch(tokenEndpoint, { method: 'POST', @@ -279,4 +282,18 @@ export class OAuth2ClientWithConfig extends OAuth2Client { throw error } } + + /** + * Get the redirect URI + */ + getRedirectUri(): string { + return this._redirectUri + } + + /** + * Get the client secret + */ + getClientSecret(): string { + return this._clientSecret + } } diff --git a/server/services/OAuth2ProviderManager.ts b/server/services/OAuth2ProviderManager.ts index 7a1fddc..57d9157 100644 --- a/server/services/OAuth2ProviderManager.ts +++ b/server/services/OAuth2ProviderManager.ts @@ -77,12 +77,7 @@ export class OAuth2ProviderManager { try { // Use OBPClientService to call the API - const response = await this.obpClientService.call( - 'GET', - '/obp/v5.1.0/well-known', - null, - null - ) + const response = await this.obpClientService.get('/obp/v5.1.0/well-known', null) if (!response.well_known_uris || response.well_known_uris.length === 0) { console.warn('OAuth2ProviderManager: No well-known URIs found in OBP API response') @@ -90,7 +85,7 @@ export class OAuth2ProviderManager { } console.log(`OAuth2ProviderManager: Found ${response.well_known_uris.length} providers:`) - response.well_known_uris.forEach((uri) => { + response.well_known_uris.forEach((uri: WellKnownUri) => { console.log(` - ${uri.provider}: ${uri.url}`) }) @@ -219,9 +214,9 @@ export class OAuth2ProviderManager { const checkPromises: Promise[] = [] - for (const [providerName, client] of this.providers.entries()) { + this.providers.forEach((client, providerName) => { checkPromises.push(this.checkProviderHealth(providerName, client)) - } + }) await Promise.allSettled(checkPromises) } @@ -288,11 +283,11 @@ export class OAuth2ProviderManager { getAvailableProviders(): string[] { const available: string[] = [] - for (const [name, status] of this.providerStatus.entries()) { + this.providerStatus.forEach((status, name) => { if (status.available && this.providers.has(name)) { available.push(name) } - } + }) return available }