Skip to content

[Duplicate Code] OidcTokenProvider (Azure) duplicates BaseOidcTokenProvider lifecycle methods #3237

Description

@github-actions

Duplicate Code Opportunity

Summary

  • Pattern: OidcTokenProvider (Azure OIDC) manually reimplements the same token lifecycle methods — initialize(), isReady(), shutdown(), _scheduleRefresh(), _sleep(), and all the associated state variables — that already exist in BaseOidcTokenProvider. The AWS and GCP providers already extend BaseOidcTokenProvider; the Azure provider was not migrated.
  • Locations: containers/api-proxy/oidc-token-provider.js (lines 42–270) vs containers/api-proxy/oidc-token-provider-base.js (entire file)
  • Impact: ~60 lines of duplicated security-critical auth-lifecycle logic; any bug fix or behaviour change in BaseOidcTokenProvider (e.g. retry logic, shutdown safety, refresh scheduling) must also be applied manually to OidcTokenProvider or it silently diverges.

Evidence

oidc-token-provider-base.js (BaseOidcTokenProvider) — already extracted:

class BaseOidcTokenProvider {
  constructor(providerPrefix, config) {
    this._retryDelayMs = config.retryDelayMs ?? REFRESH_RETRY_DELAY_MS;
    this._maxInitRetries = config.maxInitRetries ?? MAX_INIT_RETRIES;
    this._expiresAt = 0;
    this._refreshTimer = null;
    this._refreshInFlight = null;
    this._initialized = false;
    this._initError = null;
  }
  async initialize() { /* retry loop with _doRefresh() */ }
  isReady()   { return !!(this._getCachedValue() && this._expiresAt > now); }
  shutdown()  { clearTimeout(this._refreshTimer); this._refreshTimer = null; }
  _scheduleRefresh(delayMs) { /* setTimeout + unref */ }
  // Abstract: _doRefresh(), _getCachedValue(), _getInitSuccessLogContext(), ...
}

oidc-token-provider.js (OidcTokenProvider / Azure) — duplicate copy:

class OidcTokenProvider {           // ← does NOT extend BaseOidcTokenProvider
  constructor(config) {
    // ... Azure-specific fields ...
    this._expiresAt = 0;            // ← duplicated state
    this._refreshTimer = null;      // ← duplicated state
    this._refreshInFlight = null;   // ← duplicated state
    this._initialized = false;      // ← duplicated state
    this._initError = null;         // ← duplicated state
  }
  async initialize() { /* ~30 lines — same retry loop */ }   // ← duplicated
  getToken()  { /* checks _cachedToken / triggers refresh */ } // ← duplicated
  isReady()   { /* same logic */ }                            // ← duplicated
  shutdown()  { /* same logic */ }                            // ← duplicated
  _scheduleRefresh(delayMs) { /* ~20 lines — same pattern */ } // ← duplicated
  _sleep(ms)  { return new Promise(resolve => setTimeout(resolve, ms)); } // ← duplicated
}

Meanwhile AwsOidcTokenProvider and GcpOidcTokenProvider do extend BaseOidcTokenProvider:

class AwsOidcTokenProvider extends BaseOidcTokenProvider { ... }  // containers/api-proxy/aws-oidc-token-provider.js:48
class GcpOidcTokenProvider extends BaseOidcTokenProvider { ... }  // containers/api-proxy/gcp-oidc-token-provider.js:10

Suggested Refactoring

Migrate OidcTokenProvider to extend BaseOidcTokenProvider, implementing only the abstract hook methods:

const { BaseOidcTokenProvider, REFRESH_FACTOR, MIN_REFRESH_MARGIN_SECS } = require('./oidc-token-provider-base');

class OidcTokenProvider extends BaseOidcTokenProvider {
  constructor(config) {
    super('oidc', config);  // base handles retry/timer state
    // Azure-specific fields only
    this._tenantId   = config.tenantId;
    this._clientId   = config.clientId;
    this._oidcAudience = config.oidcAudience || 'api://AzureADTokenExchange';
    this._azureScope = config.azureScope || '(cognitiveservices.azure.com/redacted)
    this._loginHost  = this._resolveLoginHost(config.azureCloud);
    this._requestUrl   = config.requestUrl;
    this._requestToken = config.requestToken;
    this._cachedToken  = null;
  }

  getToken() { return this._getCachedValue(); }

  _getCachedValue() { return this._cachedToken; }

  async _doRefresh() {
    // existing _refreshToken() logic → updates this._cachedToken / this._expiresAt
  }

  _getInitSuccessLogContext() { ... }
  _getInitFailureLogContext() { ... }
}

The _scheduleRefresh, initialize, isReady, and shutdown methods are then deleted from this file entirely.

Affected Files

  • containers/api-proxy/oidc-token-provider.js — lines 42–270 (class body)
  • containers/api-proxy/oidc-token-provider-base.js — provides the base that AWS and GCP already use

Effort Estimate

Medium (refactor + update existing tests in oidc-token-provider.test.js)


Detected by Duplicate Code Detector workflow. Run date: 2026-05-15

Generated by Duplicate Code Detector · ● 18.9M ·

  • expires on Jun 14, 2026, 10:01 PM UTC

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions