ADK.Auth.OAuth2 (ADK v0.0.1-alpha.1)

Copy Markdown View Source

Core OAuth2 HTTP operations: authorization URL generation, auth code exchange, and token refresh.

Uses Req for HTTP. All functions are stateless — they operate on credential structs and return updated structs. The CredentialManager orchestrates when to call these.

Token Exchange Flow

# 1. Build authorization URL (redirect user here)
url = ADK.Auth.OAuth2.authorization_url(%{
  auth_endpoint: "https://accounts.google.com/o/oauth2/v2/auth",
  client_id: "my-client-id",
  redirect_uri: "https://myapp.com/auth/callback",
  scopes: ["openid", "profile"],
  state: "random-state-string"
})

# 2. After redirect, exchange code → tokens
{:ok, updated_cred} = ADK.Auth.OAuth2.exchange_code(credential, opts)

# 3. Later, refresh expired token
{:ok, refreshed_cred} = ADK.Auth.OAuth2.refresh_token(credential, opts)

Token Expiry

Tokens include expires_at (Unix timestamp). Use expired?/1 or expires_soon?/1 to check before use. The CredentialManager handles this automatically.

Summary

Functions

Build an OAuth2 authorization URL to redirect the user to.

Perform a client credentials grant (server-to-server, no user).

Exchange an authorization code for access + refresh tokens.

Returns true if the credential's access token has expired.

Returns true if the credential will expire within the buffer window (default: 5 minutes). Proactive refresh to avoid races.

Returns true if the credential needs exchange (has auth_code but no access_token).

Refresh an expired access token using the refresh token.

Returns true if refresh is possible (credential has refresh_token + endpoint).

Types

token_response()

@type token_response() :: %{
  access_token: String.t(),
  token_type: String.t() | nil,
  expires_in: integer() | nil,
  refresh_token: String.t() | nil,
  scope: String.t() | nil,
  id_token: String.t() | nil
}

Functions

authorization_url(opts)

@spec authorization_url(keyword() | map()) :: String.t()

Build an OAuth2 authorization URL to redirect the user to.

Required options:

  • :auth_endpoint — the authorization endpoint URL
  • :client_id — OAuth2 client ID
  • :redirect_uri — callback URL

Optional options:

  • :scopes — list of requested scopes (default: [])
  • :state — CSRF state string (default: generated)
  • :access_type"offline" to request refresh token (Google-specific)
  • :extra_params — map of additional query parameters

client_credentials(cred, opts \\ [])

@spec client_credentials(
  ADK.Auth.Credential.t(),
  keyword()
) :: {:ok, ADK.Auth.Credential.t()} | {:error, term()}

Perform a client credentials grant (server-to-server, no user).

The credential must have client_id, client_secret, and token_endpoint. Scopes from the credential are used if present.

Options:

  • :scopes — override credential scopes
  • :http_opts — extra options passed to Req.post/2

exchange_code(cred, opts \\ [])

@spec exchange_code(
  ADK.Auth.Credential.t(),
  keyword()
) :: {:ok, ADK.Auth.Credential.t()} | {:error, term()}

Exchange an authorization code for access + refresh tokens.

The credential must have:

  • auth_code — the code received from the OAuth callback
  • client_id, client_secret — your OAuth app credentials
  • token_endpoint — the token URL

Returns {:ok, updated_credential} with access_token, refresh_token, and expires_at populated, and auth_code cleared.

Options:

  • :redirect_uri — must match the one used in the authorization URL
  • :http_opts — extra options passed to Req.post/2

expired?(arg1)

@spec expired?(ADK.Auth.Credential.t()) :: boolean()

Returns true if the credential's access token has expired.

Returns false if no expires_at is set (assumes still valid).

expires_soon?(credential, buffer \\ 300)

@spec expires_soon?(ADK.Auth.Credential.t(), non_neg_integer()) :: boolean()

Returns true if the credential will expire within the buffer window (default: 5 minutes). Proactive refresh to avoid races.

needs_exchange?(arg1)

@spec needs_exchange?(ADK.Auth.Credential.t()) :: boolean()

Returns true if the credential needs exchange (has auth_code but no access_token).

refresh_token(cred, opts \\ [])

@spec refresh_token(
  ADK.Auth.Credential.t(),
  keyword()
) :: {:ok, ADK.Auth.Credential.t()} | {:error, term()}

Refresh an expired access token using the refresh token.

The credential must have refresh_token, client_id, client_secret, and token_endpoint.

Options:

  • :scopes — request specific scopes (optional)
  • :http_opts — extra options passed to Req.post/2

refreshable?(arg1)

@spec refreshable?(ADK.Auth.Credential.t()) :: boolean()

Returns true if refresh is possible (credential has refresh_token + endpoint).