PhoenixKit.Integrations (phoenix_kit v1.7.102)

Copy Markdown View Source

Centralized management of external service integrations.

Stores credentials (OAuth tokens, API keys, bot tokens, etc.) using the existing PhoenixKit.Settings system with value_json JSONB storage. Each integration is a JSON blob under a key like "integration:google".

Auth types supported

  • :oauth2 — Google, Microsoft, Slack, etc. (client_id/secret + access/refresh tokens)
  • :api_key — OpenRouter, Stripe, SendGrid, etc. (single API key)
  • :key_secret — AWS, Twilio, etc. (access key + secret key)
  • :bot_token — Telegram, Discord, etc. (single bot token)
  • :credentials — SMTP, databases, etc. (freeform credential map)

Usage

# Check if a provider is connected
PhoenixKit.Integrations.connected?("google")

# Get credentials for API calls
{:ok, creds} = PhoenixKit.Integrations.get_credentials("google")
# => %{"access_token" => "ya29...", "token_type" => "Bearer", ...}

# Make an authenticated request with auto-refresh on 401
{:ok, response} = PhoenixKit.Integrations.authenticated_request("google", :get, url)

Summary

Functions

Adds a new named connection for a provider.

Make an authenticated HTTP request with automatic token refresh on 401.

Check if an integration is connected and has valid credentials.

Disconnect an integration (remove tokens, keep setup credentials).

Exchange an OAuth authorization code for tokens and save them.

Get credentials for a provider, suitable for making API calls.

Get the full integration data for a provider.

Lists all connections for a provider.

List all configured integrations (those that have saved data).

List all known providers.

Loads all connections for multiple providers in a single database query.

Persist the outcome of a connection check (manual or automatic) onto the integration record and broadcast a PubSub event.

Refresh an expired OAuth access token and save the new one.

Removes a named connection. The "default" connection cannot be removed.

Run one-time legacy migrations for all known providers.

Save setup credentials for a provider.

Returns the settings key for a provider connection.

Validate that a provider's credentials are working.

Functions

add_connection(provider_key, name, actor_uuid \\ nil)

@spec add_connection(String.t(), String.t(), String.t() | nil) ::
  {:ok, map()} | {:error, term()}

Adds a new named connection for a provider.

The name can be any string alphanumeric with hyphens (e.g., "company-drive").

authenticated_request(provider_key, method, url, opts \\ [])

@spec authenticated_request(String.t(), atom(), String.t(), keyword()) ::
  {:ok, Req.Response.t()} | {:error, term()}

Make an authenticated HTTP request with automatic token refresh on 401.

For OAuth providers: adds Bearer token, retries with refreshed token on 401. For API key providers: adds Bearer token from the api_key. For bot token providers: returns credentials for the caller to use directly.

opts are passed through to Req.request/1.

authorization_url(provider_key, redirect_uri, extra_scopes \\ nil, state \\ nil)

@spec authorization_url(String.t(), String.t(), String.t() | nil, String.t() | nil) ::
  {:ok, String.t()} | {:error, term()}

Build the OAuth authorization URL for a provider.

Accepts an optional state parameter for CSRF protection. Use PhoenixKit.Integrations.OAuth.generate_state/0 to generate one, store it in the session or socket assigns, and verify it when the callback arrives.

connected?(provider_key)

@spec connected?(String.t()) :: boolean()

Check if an integration is connected and has valid credentials.

disconnect(provider_key, actor_uuid \\ nil)

@spec disconnect(String.t(), String.t() | nil) :: :ok

Disconnect an integration (remove tokens, keep setup credentials).

For OAuth: removes access_token, refresh_token, keeps client_id/client_secret. For API key/bot token: removes the key entirely.

exchange_code(provider_key, code, redirect_uri, actor_uuid \\ nil)

@spec exchange_code(String.t(), String.t(), String.t(), String.t() | nil) ::
  {:ok, map()} | {:error, term()}

Exchange an OAuth authorization code for tokens and save them.

get_credentials(provider_key)

@spec get_credentials(String.t()) ::
  {:ok, map()} | {:error, :not_configured | :deleted}

Get credentials for a provider, suitable for making API calls.

Returns the full integration data map. The caller extracts what it needs based on the auth type (e.g., "access_token" for OAuth, "api_key" for API key).

get_integration(provider_key)

@spec get_integration(String.t()) ::
  {:ok, map()} | {:error, :not_configured | :invalid_provider_key}

Get the full integration data for a provider.

Returns the entire JSON blob including credentials, status, and metadata. Automatically migrates legacy settings keys (e.g., "document_creator_google_oauth") on first access.

list_connections(provider_key)

@spec list_connections(String.t()) :: [
  %{uuid: String.t(), name: String.t(), data: map()}
]

Lists all connections for a provider.

Returns a list of %{uuid: uuid, name: name, data: data} maps, with "default" first. The uuid is the stable identifier for the settings row.

list_integrations()

@spec list_integrations() :: [map()]

List all configured integrations (those that have saved data).

list_providers()

@spec list_providers() :: [map()]

List all known providers.

load_all_connections(provider_keys)

@spec load_all_connections([String.t()]) :: %{
  required(String.t()) => [%{uuid: String.t(), name: String.t(), data: map()}]
}

Loads all connections for multiple providers in a single database query.

More efficient than calling list_connections/1 in a loop. Returns a map of provider_key => [%{uuid, name, data}].

record_validation(provider_key, result)

@spec record_validation(String.t(), :ok | {:error, term()}) :: :ok

Persist the outcome of a connection check (manual or automatic) onto the integration record and broadcast a PubSub event.

Writes status, validation_status and last_validated_at. Safe to call from automatic code paths (e.g. failed token refresh) — it only touches the DB / broadcasts when something actually changed, avoiding write churn on the happy path.

refresh_access_token(provider_key)

@spec refresh_access_token(String.t()) :: {:ok, String.t()} | {:error, term()}

Refresh an expired OAuth access token and save the new one.

On failure, stamps the integration record with status: "error" and a human-readable validation_status so the UI reflects the broken state without waiting for an admin to click "Test Connection". On success following a previously-errored state, auto-recovers the status back to "connected".

remove_connection(provider_key, name, actor_uuid \\ nil)

@spec remove_connection(String.t(), String.t(), String.t() | nil) ::
  :ok | {:error, term()}

Removes a named connection. The "default" connection cannot be removed.

run_legacy_migrations()

@spec run_legacy_migrations() :: :ok

Run one-time legacy migrations for all known providers.

Call this at application boot (e.g., in Application.start/2) to migrate legacy settings keys to the new integration:{provider}:{name} format. Safe to call multiple times — skips providers that already have data.

save_setup(provider_key, attrs, actor_uuid \\ nil)

@spec save_setup(String.t(), map(), String.t() | nil) ::
  {:ok, map()} | {:error, term()}

Save setup credentials for a provider.

For OAuth providers, this saves client_id/client_secret. For API key providers, this saves the api_key. For bot token providers, this saves the bot_token.

Merges with existing data to preserve any previously obtained tokens. Sets status to "disconnected" if no runtime credentials exist yet.

settings_key(provider_key)

@spec settings_key(String.t()) :: String.t()

Returns the settings key for a provider connection.

Accepts "google" (returns default connection key) or "google:personal" (returns named connection key).

Examples

iex> PhoenixKit.Integrations.settings_key("google")
"integration:google:default"

iex> PhoenixKit.Integrations.settings_key("google:personal")
"integration:google:personal"

validate_connection(provider_key, actor_uuid \\ nil)

@spec validate_connection(String.t(), String.t() | nil) :: :ok | {:error, String.t()}

Validate that a provider's credentials are working.

For OAuth: calls the provider's userinfo endpoint. For API key / bot token: calls the provider's validation endpoint if defined. Returns :ok or {:error, reason}.