Lightning.Credentials.OauthValidation (Lightning v2.13.5)
View SourceCentralized OAuth token validation with structured error handling.
This module provides comprehensive OAuth token validation used across OauthToken and Credential modules to ensure consistent validation logic.
Validates OAuth 2.0 tokens according to RFC 6749 specifications with additional robustness for real-world OAuth provider variations.
Summary
Functions
Extracts scopes from OAuth token data in various formats.
Normalizes OAuth scopes from various input formats into a consistent list.
Validates that granted scopes match exactly what the user selected.
Validates OAuth token data according to OAuth 2.0 standards (RFC 6749).
Functions
Extracts scopes from OAuth token data in various formats.
Handles both string and atom keys, and both "scope" and "scopes" fields.
Parameters
token_data
- Map containing OAuth token data
Returns
{:ok, [String.t()]}
- List of extracted scopes:error
- No valid scope data found
Examples
iex> extract_scopes(%{"scope" => "read write"})
{:ok, ["read", "write"]}
iex> extract_scopes(%{scopes: ["read", "write"]})
{:ok, ["read", "write"]}
iex> extract_scopes(%{"invalid" => "data"})
:error
Normalizes OAuth scopes from various input formats into a consistent list.
Handles multiple input formats:
- nil -> []
- OauthToken struct with body containing "scope" or "scopes"
- String with delimited scopes
- List of scopes (strings or atoms)
- Single atom scope
- Any other input -> []
Returns lowercase, trimmed, non-empty scope strings with duplicates removed.
Parameters
input
- The input to normalize (various formats supported)delimiter
- String delimiter for parsing scope strings (default: " ")
Returns
[String.t()]
- List of normalized scope strings
Examples
iex> normalize_scopes("READ Write ADMIN")
["read", "write", "admin"]
iex> normalize_scopes([:read, "WRITE", " admin "])
["read", "write", "admin"]
iex> normalize_scopes(%OauthToken{body: %{"scope" => "read write"}})
["read", "write"]
@spec validate_scope_grant(map(), [String.t()]) :: :ok | {:error, Lightning.Credentials.OauthValidation.Error.t()}
Validates that granted scopes match exactly what the user selected.
Performs case-insensitive scope matching to handle OAuth provider variations.
Parameters
token_data
- The OAuth token response from the providerexpected_scopes
- List of scopes the user selected
Returns
:ok
- All expected scopes were granted{:error, %Error{}}
- Some expected scopes missing or no scope data
Examples
iex> validate_scope_grant(%{"scope" => "read write admin"}, ["read", "write"])
:ok
iex> validate_scope_grant(%{"scope" => "read"}, ["read", "write"])
{:error, %Error{type: :missing_scopes, details: %{missing_scopes: ["write"]}}}
@spec validate_token_data(any()) :: {:ok, map()} | {:error, Lightning.Credentials.OauthValidation.Error.t()}
Validates OAuth token data according to OAuth 2.0 standards (RFC 6749).
This function validates that all required OAuth fields are present and valid in the token data. Required fields: access_token, refresh_token, scope (or scopes), expires_in (or expires_at), token_type.
Expiration Fields
expires_in
: Duration in seconds until token expires (relative, preferred by OAuth 2.0)expires_at
: Absolute timestamp when token expires (Unix timestamp or ISO 8601)
Only one expiration field is required. expires_in
is preferred as it's less susceptible to
clock skew issues between client and server.
Parameters
token_data
- The OAuth token data to validate
Returns
{:ok, token_data}
- Token data is valid{:error, %Error{}}
- Token data is invalid with structured error
Examples
iex> validate_token_data(%{
...> "access_token" => "abc123",
...> "refresh_token" => "def456",
...> "token_type" => "Bearer",
...> "expires_in" => 3600, # 1 hour
...> "scope" => "read write"
...> })
{:ok, %{"access_token" => "abc123", ...}}
iex> validate_token_data(%{
...> "access_token" => "abc123",
...> "refresh_token" => "def456",
...> "token_type" => "Bearer",
...> "expires_at" => 1672531200, # Unix timestamp
...> "scope" => "read write"
...> })
{:ok, %{"access_token" => "abc123", ...}}
iex> validate_token_data(%{"invalid" => "data"})
{:error, %Error{type: :missing_access_token, message: "..."}}