# `ExOura.OAuth2`
[🔗](https://github.com/tgrk/ex_oura/blob/v3.0.1/lib/ex_oura/oauth2.ex#L1)

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
- `spo2` - SpO2 Average recorded during sleep (in doc mistakenly as spo2Daily!)

## 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"
    }

# `authorization_options`

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

# `oauth2_config`

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

# `scope`

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

# `scopes`

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

# `token_response`

```elixir
@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()
}
```

# `authorization_url`

```elixir
@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`

```elixir
@spec available_scopes() :: scopes()
```

Returns all available OAuth2 scopes.

## Examples

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

# `default_scopes`

```elixir
@spec default_scopes() :: scopes()
```

Returns the default OAuth2 scopes used when none are specified.

## Examples

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

# `get_token`

```elixir
@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`

```elixir
@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?`

```elixir
@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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
