OAuth 2.0 authentication for all Xero APIs.
Xero uses the Authorization Code Flow for server-side apps.
Access tokens expire after 30 minutes; use refresh_token/1 to renew.
The offline_access scope is required to obtain refresh tokens.
Flow
# 1. Generate auth URL
{:ok, url, state} = Xero.Auth.authorize_url()
# store state in session, redirect user to url
# 2. Exchange code for tokens
{:ok, tokens} = Xero.Auth.fetch_token(code)
# 3. List connected organisations
{:ok, connections} = Xero.Auth.connections(tokens)
tenant_id = hd(connections).tenant_id
# 4. Refresh when expired
{:ok, new_tokens} = Xero.Auth.refresh_token(tokens.refresh_token)
# 5. Revoke on disconnect
:ok = Xero.Auth.revoke_token(tokens.refresh_token)
Summary
Functions
Generates the Xero OAuth2 authorization URL with a random CSRF state.
Returns {:ok, url, state}. Store state in session and validate on callback.
Returns the list of Xero organisations the authenticated user has connected.
Each connection map contains :tenant_id, :tenant_name, :tenant_type.
Returns a valid token, refreshing automatically if expired.
Exchanges an authorization code for access + refresh tokens.
Refreshes an expired access token.
Requires the offline_access scope to have been requested originally.
Revokes a refresh token, disconnecting the user's Xero access.
Returns true if the token is expired or expiring within 60 seconds.
Functions
@spec authorize_url(keyword()) :: {:ok, String.t(), String.t()} | {:error, Xero.Error.t()}
Generates the Xero OAuth2 authorization URL with a random CSRF state.
Returns {:ok, url, state}. Store state in session and validate on callback.
Options
:scopes— override configured scopes:state— supply a custom state string (default: random 32 bytes)
@spec connections(Xero.Auth.Token.t()) :: {:ok, [map()]} | {:error, Xero.Error.t()}
Returns the list of Xero organisations the authenticated user has connected.
Each connection map contains :tenant_id, :tenant_name, :tenant_type.
@spec ensure_valid_token(Xero.Auth.Token.t()) :: {:ok, Xero.Auth.Token.t()} | {:error, Xero.Error.t()}
Returns a valid token, refreshing automatically if expired.
{:ok, valid} = Xero.Auth.ensure_valid_token(tokens)
@spec fetch_token(String.t()) :: {:ok, Xero.Auth.Token.t()} | {:error, Xero.Error.t()}
Exchanges an authorization code for access + refresh tokens.
@spec refresh_token(String.t()) :: {:ok, Xero.Auth.Token.t()} | {:error, Xero.Error.t()}
Refreshes an expired access token.
Requires the offline_access scope to have been requested originally.
@spec revoke_token(String.t()) :: :ok | {:error, Xero.Error.t()}
Revokes a refresh token, disconnecting the user's Xero access.
@spec token_expired?(Xero.Auth.Token.t()) :: boolean()
Returns true if the token is expired or expiring within 60 seconds.