Ueberauth.Strategy.IntervalsIcu.OAuth (Ueberauth intervals.icu v0.1.2)

Copy Markdown View Source

OAuth client for intervals.icu.

This module owns every HTTP conversation with intervals.icu: building the authorize URL, exchanging an authorization code for a token, and making authenticated API calls on the token's behalf.

Configuration

config :ueberauth, Ueberauth.Strategy.IntervalsIcu.OAuth,
  client_id: System.get_env("INTERVALS_ICU_CLIENT_ID"),
  client_secret: System.get_env("INTERVALS_ICU_CLIENT_SECRET")

Endpoints

Note that the authorize and token endpoints live under different path prefixes, which is why both are configured as absolute URLs:

https://intervals.icu/oauth/authorize        # authorize
https://intervals.icu/api/oauth/token        # token

Both are overridable via :authorize_url and :token_url should intervals.icu ever move them.

Passing options to Req

Anything under :req_options is merged into every request, overriding this module's own defaults. Use it to supply a custom Finch pool, a retry policy, timeouts, or a test plug:

config :ueberauth, Ueberauth.Strategy.IntervalsIcu.OAuth,
  client_id: "...",
  client_secret: "...",
  req_options: [receive_timeout: 10_000]

Retries are off by default

Req retries transient failures with backoff out of the box. This module turns that off, because an OAuth callback is an interactive request: the athlete is waiting on a redirect, so several seconds of backoff before an inevitable failure is worse than failing fast. The authorization code is also only valid for two minutes, so a long retry chain can consume the very window it is meant to protect.

If your application would rather retry, say so explicitly:

req_options: [retry: :safe_transient]

Errors

get_access_token/2 reports failures as {:error, %{key: key, message: message}}, ready to be handed to Ueberauth.Strategy.Helpers.error/2.

Summary

Functions

Builds the URL the athlete is redirected to in order to grant access.

Builds the resolved client configuration.

The default intervals.icu endpoints, useful for tests and introspection.

Performs an authenticated GET against the intervals.icu API.

Exchanges an authorization code for an access token.

Types

client()

@type client() :: %{
  client_id: String.t(),
  client_secret: String.t(),
  site: String.t(),
  authorize_url: String.t(),
  token_url: String.t(),
  redirect_uri: String.t() | nil,
  req_options: keyword()
}

error()

@type error() :: %{key: String.t(), message: String.t()}

Functions

authorize_url!(params \\ [], opts \\ [])

@spec authorize_url!(keyword(), keyword()) :: String.t()

Builds the URL the athlete is redirected to in order to grant access.

params are merged into the query string. :scope and :state are the interesting ones; :client_id, :redirect_uri and :response_type are filled in from the client unless you override them.

Examples

authorize_url!([scope: "ACTIVITY:READ", state: "csrf"],
  redirect_uri: "https://example.com/auth/intervals_icu/callback")

client(opts \\ [])

@spec client(keyword()) :: client()

Builds the resolved client configuration.

Application config is merged over the defaults, and opts over that, so a call site can always override configuration.

Raises ArgumentError when configuration is missing, is not a keyword list, or omits :client_id / :client_secret.

defaults()

@spec defaults() :: keyword()

The default intervals.icu endpoints, useful for tests and introspection.

get(token, url, headers \\ [], opts \\ [])

Performs an authenticated GET against the intervals.icu API.

url may be an absolute URL or a path, which is resolved against the configured :site. The token is sent as an Authorization: Bearer header.

Returns Req's usual {:ok, %Req.Response{}} / {:error, exception}.

Examples

get(token, "/api/v1/athlete/0")

get_access_token(params \\ [], opts \\ [])

@spec get_access_token(keyword(), keyword()) ::
  {:ok, Ueberauth.Strategy.IntervalsIcu.Token.t()} | {:error, error()}

Exchanges an authorization code for an access token.

intervals.icu expects client_id, client_secret and code as form-encoded body parameters, not HTTP Basic credentials, so that is what this sends.

The code is only valid for two minutes after the redirect.

Returns {:ok, %Token{}} or {:error, %{key: key, message: message}}.