Gemini.Auth.ADC (GeminiEx v0.8.4)

View Source

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

ADC searches for credentials in the following order:

  1. Environment Variable: GOOGLE_APPLICATION_CREDENTIALS pointing to a service account JSON file
  2. User Credentials: ~/.config/gcloud/application_default_credentials.json (created via gcloud auth application-default login)
  3. GCP Metadata Server: Automatic credentials for code running on GCP infrastructure (Compute Engine, GKE, Cloud Run, Cloud Functions, App Engine)

Features

  • Automatic credential discovery following Google's ADC standard
  • 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 Key (Development)

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

Option 2: User Credentials (Development)

gcloud auth application-default login

Option 3: 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.

Summary

Functions

Check if ADC credentials are available.

Get an access token from loaded credentials.

Get project ID from credentials if available.

Load credentials following the ADC chain.

Refresh an access token.

Types

access_token()

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

credentials()

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

error_reason()

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

metadata_server_credentials()

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

service_account_credentials()

@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()

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

Functions

available?()

@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(credentials, opts \\ [])

@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(arg1)

@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

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()

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

Load credentials following the ADC chain.

Searches for credentials in the standard ADC order:

  1. GOOGLE_APPLICATION_CREDENTIALS environment variable
  2. User credentials file (~/.config/gcloud/application_default_credentials.json)
  3. GCP metadata server

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(credentials)

@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

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)