const require = (await import('node:module')).createRequire(import.meta.url); const __chunk_filename = (await import('node:url')).fileURLToPath(import.meta.url); const __chunk_dirname = (await import('node:path')).dirname(__chunk_filename);
import {
  BaseA2AAuthProvider,
  DefaultAgentCardResolver,
  MCPOAuthTokenStorage,
  buildAuthorizationUrl,
  exchangeCodeForToken,
  generatePKCEParams,
  getConsentForOauth,
  getPortFromUrl,
  openBrowserSecurely,
  refreshAccessToken,
  startCallbackServer
} from "./chunk-DQ7NBN5H.js";
import {
  FatalCancellationError,
  Storage,
  coreEvents,
  debugLogger,
  getErrorMessage
} from "./chunk-N6QYTC2T.js";
import "./chunk-664ZODQF.js";
import "./chunk-RJTRUG2J.js";
import "./chunk-IUUIT4SU.js";
import "./chunk-34MYV7JD.js";

// packages/core/src/agents/auth-provider/oauth2-provider.ts
var OAuth2AuthProvider = class extends BaseA2AAuthProvider {
  constructor(config, agentName, agentCard, agentCardUrl) {
    super();
    this.config = config;
    this.agentName = agentName;
    this.agentCardUrl = agentCardUrl;
    this.tokenStorage = new MCPOAuthTokenStorage(
      Storage.getA2AOAuthTokensPath(),
      "gemini-cli-a2a"
    );
    this.authorizationUrl = config.authorization_url;
    this.tokenUrl = config.token_url;
    this.scopes = config.scopes;
    this.mergeAgentCardDefaults(agentCard);
  }
  type = "oauth2";
  tokenStorage;
  cachedToken = null;
  /** Resolved OAuth URLs — may come from config or agent card. */
  authorizationUrl;
  tokenUrl;
  scopes;
  /**
   * Initialize the provider by loading any persisted token from storage.
   * Also discovers OAuth URLs from the agent card if not yet resolved.
   */
  async initialize() {
    if ((!this.authorizationUrl || !this.tokenUrl) && this.agentCardUrl) {
      await this.fetchAgentCardDefaults();
    }
    const credentials = await this.tokenStorage.getCredentials(this.agentName);
    if (credentials && !this.tokenStorage.isTokenExpired(credentials.token)) {
      this.cachedToken = credentials.token;
      debugLogger.debug(
        `[OAuth2AuthProvider] Loaded valid cached token for "${this.agentName}"`
      );
    }
  }
  /**
   * Return an Authorization header with a valid Bearer token.
   * Refreshes or triggers interactive auth as needed.
   */
  async headers() {
    if (this.cachedToken && !this.tokenStorage.isTokenExpired(this.cachedToken)) {
      return { Authorization: `Bearer ${this.cachedToken.accessToken}` };
    }
    if (this.cachedToken?.refreshToken && this.tokenUrl && this.config.client_id) {
      try {
        const refreshed = await refreshAccessToken(
          {
            clientId: this.config.client_id,
            clientSecret: this.config.client_secret,
            scopes: this.scopes
          },
          this.cachedToken.refreshToken,
          this.tokenUrl
        );
        this.cachedToken = this.toOAuthToken(
          refreshed,
          this.cachedToken.refreshToken
        );
        await this.persistToken();
        return { Authorization: `Bearer ${this.cachedToken.accessToken}` };
      } catch (error) {
        debugLogger.debug(
          `[OAuth2AuthProvider] Refresh failed, falling back to interactive flow: ${getErrorMessage(error)}`
        );
        await this.tokenStorage.deleteCredentials(this.agentName);
      }
    }
    this.cachedToken = await this.authenticateInteractively();
    return { Authorization: `Bearer ${this.cachedToken.accessToken}` };
  }
  /**
   * On 401/403, clear the cached token and re-authenticate (up to MAX_AUTH_RETRIES).
   */
  async shouldRetryWithHeaders(_req, res) {
    if (res.status !== 401 && res.status !== 403) {
      this.authRetryCount = 0;
      return void 0;
    }
    if (this.authRetryCount >= BaseA2AAuthProvider.MAX_AUTH_RETRIES) {
      return void 0;
    }
    this.authRetryCount++;
    debugLogger.debug(
      "[OAuth2AuthProvider] Auth failure, clearing token and re-authenticating"
    );
    this.cachedToken = null;
    await this.tokenStorage.deleteCredentials(this.agentName);
    return this.headers();
  }
  // ---------------------------------------------------------------------------
  // Private helpers
  // ---------------------------------------------------------------------------
  /**
   * Merge authorization_url, token_url, and scopes from the agent card's
   * `securitySchemes` when not already provided via user config.
   */
  mergeAgentCardDefaults(agentCard) {
    if (!agentCard?.securitySchemes) return;
    for (const scheme of Object.values(agentCard.securitySchemes)) {
      if (scheme.type === "oauth2" && scheme.flows.authorizationCode) {
        const flow = scheme.flows.authorizationCode;
        this.authorizationUrl ??= flow.authorizationUrl;
        this.tokenUrl ??= flow.tokenUrl;
        this.scopes ??= Object.keys(flow.scopes);
        break;
      }
    }
  }
  /**
   * Fetch the agent card from `agentCardUrl` using `DefaultAgentCardResolver`
   * (which normalizes proto-format cards) and extract OAuth2 URLs.
   */
  async fetchAgentCardDefaults() {
    if (!this.agentCardUrl) return;
    try {
      debugLogger.debug(
        `[OAuth2AuthProvider] Fetching agent card from ${this.agentCardUrl}`
      );
      const resolver = new DefaultAgentCardResolver();
      const card = await resolver.resolve(this.agentCardUrl, "");
      this.mergeAgentCardDefaults(card);
    } catch (error) {
      debugLogger.warn(
        `[OAuth2AuthProvider] Could not fetch agent card for OAuth URL discovery: ${getErrorMessage(error)}`
      );
    }
  }
  /**
   * Run a full OAuth 2.0 Authorization Code + PKCE flow through the browser.
   */
  async authenticateInteractively() {
    if (!this.config.client_id) {
      throw new Error(
        `OAuth2 authentication for agent "${this.agentName}" requires a client_id. Add client_id to the auth config in your agent definition.`
      );
    }
    if (!this.authorizationUrl || !this.tokenUrl) {
      throw new Error(
        `OAuth2 authentication for agent "${this.agentName}" requires authorization_url and token_url. Provide them in the auth config or ensure the agent card exposes an oauth2 security scheme.`
      );
    }
    const flowConfig = {
      clientId: this.config.client_id,
      clientSecret: this.config.client_secret,
      authorizationUrl: this.authorizationUrl,
      tokenUrl: this.tokenUrl,
      scopes: this.scopes
    };
    const pkceParams = generatePKCEParams();
    const preferredPort = getPortFromUrl(flowConfig.redirectUri);
    const callbackServer = startCallbackServer(pkceParams.state, preferredPort);
    const redirectPort = await callbackServer.port;
    const authUrl = buildAuthorizationUrl(
      flowConfig,
      pkceParams,
      redirectPort,
      /* resource= */
      void 0
      // No MCP resource parameter for A2A.
    );
    const consent = await getConsentForOauth(
      `Authentication required for A2A agent: '${this.agentName}'.`
    );
    if (!consent) {
      throw new FatalCancellationError("Authentication cancelled by user.");
    }
    coreEvents.emitFeedback(
      "info",
      `\u2192 Opening your browser for OAuth sign-in...

If the browser does not open, copy and paste this URL into your browser:
${authUrl}

\u{1F4A1} TIP: Triple-click to select the entire URL, then copy and paste it into your browser.
\u26A0\uFE0F  Make sure to copy the COMPLETE URL - it may wrap across multiple lines.`
    );
    try {
      await openBrowserSecurely(authUrl);
    } catch (error) {
      debugLogger.warn(
        "Failed to open browser automatically:",
        getErrorMessage(error)
      );
    }
    const { code } = await callbackServer.response;
    debugLogger.debug(
      "\u2713 Authorization code received, exchanging for tokens..."
    );
    const tokenResponse = await exchangeCodeForToken(
      flowConfig,
      code,
      pkceParams.codeVerifier,
      redirectPort,
      /* resource= */
      void 0
    );
    if (!tokenResponse.access_token) {
      throw new Error("No access token received from token endpoint");
    }
    const token = this.toOAuthToken(tokenResponse);
    this.cachedToken = token;
    await this.persistToken();
    debugLogger.debug("\u2713 OAuth2 authentication successful! Token saved.");
    return token;
  }
  /**
   * Convert an `OAuthTokenResponse` into the internal `OAuthToken` format.
   */
  toOAuthToken(response, fallbackRefreshToken) {
    const token = {
      accessToken: response.access_token,
      tokenType: response.token_type || "Bearer",
      refreshToken: response.refresh_token || fallbackRefreshToken,
      scope: response.scope
    };
    if (response.expires_in) {
      token.expiresAt = Date.now() + response.expires_in * 1e3;
    }
    return token;
  }
  /**
   * Persist the current cached token to disk.
   */
  async persistToken() {
    if (!this.cachedToken) return;
    await this.tokenStorage.saveToken(
      this.agentName,
      this.cachedToken,
      this.config.client_id,
      this.tokenUrl
    );
  }
};
export {
  OAuth2AuthProvider
};
/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
