# `Gemini.Auth.ADC`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/auth/adc.ex#L1)

Application Default Credentials (ADC) for Google Cloud authentication.

This module implements Google's Application Default Credentials (ADC) strategy,
which provides a standardized way to obtain credentials for Google Cloud APIs
without hardcoding authentication details in your application.

## ADC Credential Search Order

gemini_ex searches for credentials in the following order:

1. **Environment Variable (JSON, gemini_ex extension)**:
   `GOOGLE_APPLICATION_CREDENTIALS_JSON` containing the service account JSON
   content directly (useful for containerized environments)
2. **Environment Variable (Path, standard ADC)**: `GOOGLE_APPLICATION_CREDENTIALS` pointing to a
   service account JSON file
3. **User Credentials (standard ADC)**: `~/.config/gcloud/application_default_credentials.json`
   (created via `gcloud auth application-default login`)
4. **GCP Metadata Server (standard ADC)**: Automatic credentials for code running on GCP
   infrastructure (Compute Engine, GKE, Cloud Run, Cloud Functions, App Engine)

## Features

- Automatic credential discovery following ADC conventions
- Token caching with automatic refresh
- Support for service account and user credentials
- Metadata server authentication for GCP workloads
- Thread-safe token management

## Usage

    # Load credentials using ADC
    case ADC.load_credentials() do
      {:ok, credentials} ->
        # Get an access token
        case ADC.get_access_token(credentials) do
          {:ok, token} ->
            # Use token for API calls
            make_api_call(token)

          {:error, reason} ->
            Logger.error("Failed to get token: #{reason}")
        end

      {:error, reason} ->
        Logger.error("No credentials found: #{reason}")
    end

## Setting Up ADC

### Option 1: Service Account JSON Content (Containerized Environments)

    export GOOGLE_APPLICATION_CREDENTIALS_JSON='{"type":"service_account",...}'

### Option 2: Service Account Key File (Development)

    export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"

### Option 3: User Credentials (Development)

    gcloud auth application-default login

### Option 4: Metadata Server (Production on GCP)

    # No setup required - automatically works on GCP infrastructure

## Token Caching

Access tokens are automatically cached with a 5-minute refresh buffer.
This means a token with a 1-hour TTL will be refreshed after 55 minutes,
ensuring your application never uses an expired token.

# `access_token`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/auth/adc.ex#L128)

```elixir
@type access_token() :: String.t()
```

# `credentials`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/auth/adc.ex#L97)

```elixir
@type credentials() ::
  {:service_account, service_account_credentials()}
  | {:user, user_credentials()}
  | {:metadata_server, metadata_server_credentials()}
```

# `error_reason`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/auth/adc.ex#L129)

```elixir
@type error_reason() :: String.t()
```

# `metadata_server_credentials`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/auth/adc.ex#L123)

```elixir
@type metadata_server_credentials() :: %{
  source: :metadata_server,
  project_id: String.t() | nil
}
```

# `service_account_credentials`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/auth/adc.ex#L102)

```elixir
@type service_account_credentials() :: %{
  type: String.t(),
  project_id: String.t(),
  private_key_id: String.t(),
  private_key: String.t(),
  client_email: String.t(),
  client_id: String.t(),
  auth_uri: String.t(),
  token_uri: String.t(),
  auth_provider_x509_cert_url: String.t(),
  client_x509_cert_url: String.t()
}
```

# `user_credentials`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/auth/adc.ex#L115)

```elixir
@type user_credentials() :: %{
  type: String.t(),
  client_id: String.t(),
  client_secret: String.t(),
  refresh_token: String.t(),
  quota_project_id: String.t() | nil
}
```

# `available?`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/auth/adc.ex#L378)

```elixir
@spec available?() :: boolean()
```

Check if ADC credentials are available.

Performs a quick check to see if any credentials can be found via ADC,
without actually loading them.

## Returns

- `true` - Credentials are available
- `false` - No credentials found

## Examples

    if ADC.available?() do
      Logger.info("ADC credentials available")
    else
      Logger.warning("No ADC credentials found")
    end

# `get_access_token`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/auth/adc.ex#L228)

```elixir
@spec get_access_token(
  credentials(),
  keyword()
) :: {:ok, access_token()} | {:error, error_reason()}
```

Get an access token from loaded credentials.

Attempts to retrieve a cached token first. If no cached token exists
or the cached token is expired, generates a new token and caches it.

## Parameters

- `credentials`: Credentials tuple from `load_credentials/0`
- `opts`: Optional keyword list
  - `:force_refresh` - Skip cache and force token refresh (default: false)
  - `:cache_key` - Custom cache key (default: auto-generated)

## Returns

- `{:ok, access_token}` - Access token retrieved successfully
- `{:error, reason}` - Failed to get access token

## Examples

    {:ok, creds} = ADC.load_credentials()

    # Get token (uses cache if available)
    {:ok, token} = ADC.get_access_token(creds)

    # Force refresh
    {:ok, fresh_token} = ADC.get_access_token(creds, force_refresh: true)

# `get_project_id`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/auth/adc.ex#L336)

```elixir
@spec get_project_id(credentials()) :: {:ok, String.t()} | {:error, error_reason()}
```

Get project ID from credentials if available.

Extracts the project ID from the loaded credentials. Useful for
configuring Vertex AI which requires a project ID.

## Parameters

- `credentials`: Credentials tuple from `load_credentials/0`

## Returns

- `{:ok, project_id}` - Project ID extracted successfully
- `{:error, reason}` - No project ID available in credentials

## Examples

    {:ok, creds} = ADC.load_credentials()

    case ADC.get_project_id(creds) do
      {:ok, project_id} ->
        # Use for Vertex AI
        %{project_id: project_id, location: "us-central1"}

      {:error, _} ->
        # Prompt user or use environment variable
        System.get_env("VERTEX_PROJECT_ID")
    end

# `load_credentials`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/auth/adc.ex#L162)

```elixir
@spec load_credentials() :: {:ok, credentials()} | {:error, error_reason()}
```

Load credentials following the ADC chain.

Searches for credentials in this order:
1. GOOGLE_APPLICATION_CREDENTIALS_JSON environment variable (JSON content, gemini_ex extension)
2. GOOGLE_APPLICATION_CREDENTIALS environment variable (file path, standard ADC)
3. User credentials file (~/.config/gcloud/application_default_credentials.json, standard ADC)
4. GCP metadata server (standard ADC)

## Returns

- `{:ok, credentials}` - Credentials found and loaded
- `{:error, reason}` - No credentials found or loading failed

## Examples

    case ADC.load_credentials() do
      {:ok, {:service_account, creds}} ->
        Logger.info("Using service account: #{creds.client_email}")

      {:ok, {:user, creds}} ->
        Logger.info("Using user credentials")

      {:ok, {:metadata_server, creds}} ->
        Logger.info("Using metadata server")

      {:error, reason} ->
        Logger.error("No credentials found: #{reason}")
    end

# `refresh_token`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/auth/adc.ex#L302)

```elixir
@spec refresh_token(credentials()) :: {:ok, access_token()} | {:error, error_reason()}
```

Refresh an access token.

Forces a token refresh regardless of whether a cached token exists.
This is equivalent to calling `get_access_token/2` with `force_refresh: true`.

## Parameters

- `credentials`: Credentials tuple from `load_credentials/0`

## Returns

- `{:ok, access_token}` - New access token generated successfully
- `{:error, reason}` - Failed to refresh token

## Examples

    {:ok, creds} = ADC.load_credentials()
    {:ok, fresh_token} = ADC.refresh_token(creds)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
