OAuth2 integration library for Elixir applications.
Provides OAuth2 Authorization Code Flow with PKCE support, multi-tenant token management, and provider configuration.
Ready-to-Use API
For Phoenix applications, Tango provides a complete OAuth API that can be mounted with a single line:
# In your Phoenix router
scope "/api/oauth" do
pipe_through :api
forward "/", Tango.API.Router
endSee Tango.API for complete setup and usage documentation.
Direct Library Usage
You can also use Tango's functions directly in your application code:
Summary
Functions
Generates OAuth authorization URL with PKCE support.
Cleans up expired OAuth sessions.
Creates a new provider.
Creates provider from Nango configuration.
Creates OAuth session for provider and tenant.
Soft deletes a provider.
Exchanges authorization code for access token.
Gets connection for provider and tenant.
Gets provider by name.
Gets provider by ID.
Gets OAuth session by token.
Lists active connections for a tenant.
Lists active OAuth providers.
Marks connection as used (updates timestamp).
Records the provider rejecting a connection's credentials.
Clears a connection's rejection streak after a call authenticates again.
Refreshes an OAuth connection's access token.
Refreshes connections about to expire.
Revokes a connection for a tenant.
Updates a provider.
Functions
Generates OAuth authorization URL with PKCE support.
Examples
iex> Tango.authorize_url("session_token", redirect_uri: "https://app.com/callback")
{:ok, "https://provider.com/oauth/authorize?..."}
Cleans up expired OAuth sessions.
Examples
iex> Tango.cleanup_expired_sessions()
{:ok, 5} # 5 sessions cleaned up
Creates a new provider.
Examples
iex> Tango.create_provider(%{name: "custom", display_name: "Custom OAuth"})
{:ok, %Tango.Schemas.Provider{}}
Creates provider from Nango configuration.
Examples
iex> Tango.create_provider_from_nango("github", nango_config, client_id: "abc", client_secret: "xyz")
{:ok, %Tango.Schemas.Provider{}}
Creates OAuth session for provider and tenant.
Returns session that can be used to generate authorization URL.
Examples
iex> Tango.create_session("github", "user-123")
{:ok, %Tango.Schemas.OAuthSession{}}
iex> Tango.create_session("nonexistent", "user-123")
{:error, :provider_not_found}
Soft deletes a provider.
Examples
iex> Tango.delete_provider(provider)
{:ok, %Tango.Schemas.Provider{active: false}}
Exchanges authorization code for access token.
Validates session state, exchanges code with provider, and creates connection with multi-tenant isolation.
Examples
iex> Tango.exchange_code("state", "code", "tenant-123", redirect_uri: "https://app.com/callback")
{:ok, %Tango.Schemas.Connection{}}
Gets connection for provider and tenant.
Examples
iex> Tango.get_connection_for_provider("github", "user-123")
{:ok, %Tango.Schemas.Connection{}}
iex> Tango.get_connection_for_provider("github", "user-123", auto_refresh: true)
{:ok, %Tango.Schemas.Connection{}} # Automatically refreshed if needed
Gets provider by name.
Examples
iex> Tango.get_provider("github")
{:ok, %Tango.Schemas.Provider{}}
Gets provider by ID.
Examples
iex> Tango.get_provider_by_id(provider_id)
{:ok, %Tango.Schemas.Provider{}}
Gets OAuth session by token.
Examples
iex> Tango.get_session("session_token")
{:ok, %Tango.Schemas.OAuthSession{}}
Lists active connections for a tenant.
Examples
iex> Tango.list_connections("user-123")
[%Tango.Schemas.Connection{}, ...]
Lists active OAuth providers.
Examples
iex> Tango.list_providers()
[%Tango.Schemas.Provider{}, ...]
Marks connection as used (updates timestamp).
Examples
iex> Tango.mark_connection_used(connection)
{:ok, %Tango.Schemas.Connection{}}
Records the provider rejecting a connection's credentials.
Call this when a provider rejects the connection's credentials outright — an
HTTP 401, or a 403 whose provider-specific error body confirms an invalid or
revoked token rather than a policy, scope, or quota denial. Tango cannot see
the calls made with the tokens it issues, so a revoked credential stays
:active until a consumer reports the rejection. The connection is expired
once the rejections reach opts[:max_failures] (default 3), after which
get_connection_for_provider/2 returns {:error, :not_found}. Pass a
threshold matched to how often the caller hits that provider — the count is a
number of rejections, not a duration.
Accepts a connection, or a connection id together with opts[:tenant_id].
Only active connections accept a report.
Examples
iex> Tango.record_auth_failure(connection, "http_401")
{:ok, %Tango.Schemas.Connection{auth_failures: 1}}
iex> Tango.record_auth_failure(connection_id, "http_401", tenant_id: "user-123", max_failures: 10)
{:ok, %Tango.Schemas.Connection{auth_failures: 1}}
Clears a connection's rejection streak after a call authenticates again.
Pairs with record_auth_failure/3 to keep its count consecutive. Accepts a
connection, or a connection id together with opts[:tenant_id].
Examples
iex> Tango.record_auth_success(connection)
:ok
Refreshes an OAuth connection's access token.
Examples
iex> Tango.refresh_connection(connection)
{:ok, %Tango.Schemas.Connection{}}
Refreshes connections about to expire.
Examples
iex> Tango.refresh_expiring_connections()
{:ok, 3} # 3 connections refreshed
Revokes a connection for a tenant.
Examples
iex> Tango.revoke_connection(connection, "user-123")
{:ok, %Tango.Schemas.Connection{status: :revoked}}
Updates a provider.
Examples
iex> Tango.update_provider(provider, %{display_name: "New Name"})
{:ok, %Tango.Schemas.Provider{}}