ExOura.OAuth2 (ex_oura v2.0.0)

View Source

OAuth2 flow implementation for Oura API v2.

Provides functions for OAuth2 authorization code flow including:

  • Authorization URL generation
  • Token exchange (authorization code for access token)
  • Token refresh
  • Token storage and retrieval

Configuration

Configure your OAuth2 application credentials:

config :ex_oura,
  oauth2: [
    client_id: "your_client_id",
    client_secret: "your_client_secret",
    redirect_uri: "your_redirect_uri"
  ]

Or set them as environment variables:

  • OURA_CLIENT_ID
  • OURA_CLIENT_SECRET
  • OURA_REDIRECT_URI

Usage

Step 1: Generate authorization URL

authorization_url = ExOura.OAuth2.authorization_url([
  scopes: ["daily", "heartrate", "personal"],
  state: "your_state_parameter"
])

Step 2: Exchange authorization code for tokens

{:ok, tokens} = ExOura.OAuth2.get_token("authorization_code_from_callback")

Step 3: Use the access token with the client

{:ok, client} = ExOura.Client.start_link(tokens.access_token)

Step 4: Refresh tokens when they expire

{:ok, new_tokens} = ExOura.OAuth2.refresh_token(tokens.refresh_token)

Available Scopes

  • email - Email address of the user
  • personal - Personal information (gender, age, height, weight)
  • daily - Daily summaries of sleep, activity and readiness
  • heartrate - Time series heart rate for Gen 3 users
  • workout - Summaries for auto-detected and user entered workouts
  • tag - User entered tags
  • session - Guided and unguided sessions in the Oura app
  • spo2Daily - SpO2 Average recorded during sleep

Token Structure

Tokens are returned as a map with the following structure:

%{
  access_token: "access_token",
  refresh_token: "refresh_token",
  token_type: "Bearer",
  expires_in: 86400,
  expires_at: ~U[2025-08-26 12:00:00Z],
  scope: "daily heartrate personal"
}

Summary

Functions

Generates the OAuth2 authorization URL to redirect users to.

Returns all available OAuth2 scopes.

Returns the default OAuth2 scopes used when none are specified.

Exchanges an authorization code for access and refresh tokens.

Refreshes an access token using a refresh token.

Checks if an access token is expired or will expire soon.

Types

authorization_options()

@type authorization_options() :: [scopes: scopes(), state: String.t() | nil]

oauth2_config()

@type oauth2_config() :: [
  client_id: String.t(),
  client_secret: String.t(),
  redirect_uri: String.t()
]

scope()

@type scope() :: String.t()

scopes()

@type scopes() :: [scope()]

token_response()

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

Functions

authorization_url(opts \\ [])

@spec authorization_url(authorization_options()) :: String.t()

Generates the OAuth2 authorization URL to redirect users to.

Options

  • :scopes - List of scopes to request (defaults to ["personal", "daily"])
  • :state - State parameter for CSRF protection (recommended)

Examples

iex> ExOura.OAuth2.authorization_url()
"https://cloud.ouraring.com/oauth/authorize?client_id=your_id&redirect_uri=your_uri&response_type=code&scope=personal+daily"

iex> ExOura.OAuth2.authorization_url(scopes: ["daily", "heartrate"], state: "random_state")
"https://cloud.ouraring.com/oauth/authorize?client_id=your_id&redirect_uri=your_uri&response_type=code&scope=daily+heartrate&state=random_state"

available_scopes()

@spec available_scopes() :: scopes()

Returns all available OAuth2 scopes.

Examples

ExOura.OAuth2.available_scopes()
# => ["email", "personal", "daily", "heartrate", "workout", "tag", "session", "spo2Daily"]

default_scopes()

@spec default_scopes() :: scopes()

Returns the default OAuth2 scopes used when none are specified.

Examples

ExOura.OAuth2.default_scopes()
# => ["personal", "daily"]

get_token(code, opts \\ [])

@spec get_token(String.t(), Keyword.t()) :: {:ok, token_response()} | {:error, term()}

Exchanges an authorization code for access and refresh tokens.

Parameters

  • code - The authorization code received from the callback
  • opts - Additional options (currently unused)

Returns

{:ok, tokens} on success or {:error, reason} on failure.

Examples

{:ok, tokens} = ExOura.OAuth2.get_token("authorization_code_from_callback")

tokens.access_token
# => "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

tokens.expires_at
# => ~U[2025-08-26 12:00:00Z]

refresh_token(refresh_token, opts \\ [])

@spec refresh_token(String.t(), Keyword.t()) ::
  {:ok, token_response()} | {:error, term()}

Refreshes an access token using a refresh token.

Parameters

  • refresh_token - The refresh token to use
  • opts - Additional options (currently unused)

Returns

{:ok, tokens} on success or {:error, reason} on failure.

Examples

{:ok, new_tokens} = ExOura.OAuth2.refresh_token(old_tokens.refresh_token)

token_expired?(tokens, buffer_seconds \\ 300)

@spec token_expired?(token_response(), integer()) :: boolean()

Checks if an access token is expired or will expire soon.

Parameters

  • tokens - Token map with expires_at field
  • buffer_seconds - Seconds before expiry to consider expired (default: 300)

Examples

ExOura.OAuth2.token_expired?(tokens)
# => false

ExOura.OAuth2.token_expired?(tokens, 3600)  # 1 hour buffer
# => true