Ltix.OAuth.AccessToken (Ltix v0.2.0)

Copy Markdown View Source

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]

Summary

Functions

Check whether the token has expired.

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

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

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

Types

t()

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

Functions

expired?(access_token)

@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(map)

@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(body, opts \\ [])

@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(token)

@spec to_map(t()) :: %{required(String.t()) => 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"
}