# `Ltix.OAuth.AccessToken`
[🔗](https://github.com/DecoyLex/ltix/blob/main/lib/ltix/oauth/access_token.ex#L1)

OAuth 2.0 access token response.

A cacheable struct holding the parsed token data from a platform's token
endpoint. Host apps can store this and reuse it across contexts via
`Ltix.OAuth.Client.from_access_token/2`. To store it outside the VM, such as
in a database, save `to_map/1` as JSON and restore it with `from_map/1`.

## Examples

    iex> {:ok, token} = Ltix.OAuth.AccessToken.from_response(
    ...>   %{
    ...>     "access_token" => "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9",
    ...>     "token_type" => "Bearer",
    ...>     "expires_in" => 3600,
    ...>     "scope" => "https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly"
    ...>   },
    ...>   now: ~U[2025-01-01 00:00:00Z]
    ...> )
    iex> token.access_token
    "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9"
    iex> token.token_type
    "bearer"
    iex> token.granted_scopes
    MapSet.new(["https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly"])
    iex> token.expires_at
    ~U[2025-01-01 01:00:00Z]

# `t`

```elixir
@type t() :: %Ltix.OAuth.AccessToken{
  access_token: String.t(),
  expires_at: DateTime.t(),
  granted_scopes: MapSet.t(String.t()),
  issued_at: DateTime.t() | nil,
  token_type: String.t()
}
```

# `expired?`

```elixir
@spec expired?(t()) :: boolean()
```

Check whether the token has expired.

A token counts as expired 60 seconds before its expiry, so it isn't used
when about to expire. A token with a lifetime under two minutes counts as
expired halfway through its lifetime. Without an `issued_at` time,
the 60 seconds apply.

## Examples

    iex> token = %Ltix.OAuth.AccessToken{expires_at: DateTime.add(DateTime.utc_now(), 3600)}
    iex> Ltix.OAuth.AccessToken.expired?(token)
    false

# `from_map`

```elixir
@spec from_map(map()) :: {:ok, t()} | {:error, Exception.t()}
```

Restore a token from a map returned by `to_map/1`.

Returns `Ltix.Errors.Invalid.InvalidField` for a missing or malformed
field.

## Examples

    iex> {:ok, token} = Ltix.OAuth.AccessToken.from_map(%{
    ...>   "access_token" => "tok",
    ...>   "scope" => "scope:read",
    ...>   "expires_at" => "2025-01-01T01:00:00Z"
    ...> })
    iex> {token.access_token, token.granted_scopes, token.expires_at}
    {"tok", MapSet.new(["scope:read"]), ~U[2025-01-01 01:00:00Z]}

# `from_response`

```elixir
@spec from_response(term(), keyword()) :: {:ok, t()} | {:error, Exception.t()}
```

Parse a bearer access token from a token endpoint response body.

If `"expires_in"` is omitted, the lifetime defaults to 3600 seconds.

## Options

  * `:requested_scopes` - list of scope strings that were requested, used
    when the response omits `"scope"`.
  * `:now` - override current time (defaults to `DateTime.utc_now/0`).

# `to_map`

```elixir
@spec to_map(t()) :: %{required(String.t()) =&gt; String.t()}
```

Convert a token to a map with string keys and values that encodes to JSON.

Restore it with `from_map/1`.

## Examples

    iex> token = %Ltix.OAuth.AccessToken{
    ...>   access_token: "tok",
    ...>   token_type: "bearer",
    ...>   granted_scopes: MapSet.new(["scope:write", "scope:read"]),
    ...>   expires_at: ~U[2025-01-01 01:00:00Z]
    ...> }
    iex> Ltix.OAuth.AccessToken.to_map(token)
    %{
      "access_token" => "tok",
      "scope" => "scope:read scope:write",
      "expires_at" => "2025-01-01T01:00:00Z"
    }

---

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