Packages
indieweb
0.0.46
0.0.66
0.0.65
0.0.64
0.0.63
0.0.62
0.0.61
0.0.60
0.0.59
0.0.58
0.0.56
0.0.55
0.0.54
0.0.53
0.0.51
0.0.50
0.0.49
0.0.48
0.0.47
0.0.46
0.0.42
0.0.41
0.0.40
0.0.39
0.0.38
0.0.37
0.0.36
0.0.35
0.0.34
0.0.33
0.0.32
0.0.31
0.0.30
0.0.29
0.0.28
0.0.27
0.0.26
0.0.25
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.3
0.0.2
0.0.1
Collection of common IndieWeb utilites like authorship resolution, Webmention, post type discovery and IndieAuth.
Current section
Files
Jump to
Current section
Files
lib/indieweb/auth.ex
defmodule IndieWeb.Auth do
@moduledoc """
Provides logic for handling [IndieAuth](https://indieauth.spec.indieweb.org) interactions.
"""
@doc "Provides endpoint information for well known endpoints in IndieAuth."
@spec endpoint_for(atom(), binary()) :: binary() | nil
def endpoint_for(type, uri)
def endpoint_for(component, url) when component in ~w(authorization token)a do
IndieWeb.LinkRel.find(url, "#{component}_endpoint") |> List.first()
end
def endpoint_for(:redirect_uri, url),
do: IndieWeb.LinkRel.find(url, "redirect_uri")
def endpoint_for(_, _), do: nil
@doc """
Generates an authentication URL.
Depending on the particular `response_type`, this will generate a URL
that can be used to sign in a user.
"""
@spec authenticate(map(), keyword()) :: {:ok, any()} | {:error, any()}
def authenticate(params, options \\ [])
def authenticate(%{"response_type" => "id"} = params, options) do
case do_validate_request(params, ~w(client_id redirect_uri state me), options) do
{:error, _} = error ->
error
{:ok,
%{
"client_id" => client_id,
"redirect_uri" => redirect_uri,
"state" => state
}} ->
code = IndieWeb.Auth.Code.generate(client_id, redirect_uri, %{}, options)
do_generate_redirect_uri(redirect_uri, code, state)
end
end
def authenticate(%{"response_type" => "code"} = params, options) do
case do_validate_request(params, ~w(client_id redirect_uri state me), options) do
{:error, _} = error ->
error
{:ok,
%{
"client_id" => client_id,
"redirect_uri" => redirect_uri,
"state" => state
} = args} ->
scope = Map.get(args, "scope", "read")
code =
IndieWeb.Auth.Code.generate(
client_id,
redirect_uri,
%{
"scope" => scope
},
options
)
do_generate_redirect_uri(redirect_uri, code, state)
end
end
def authenticate(_, _), do: {:error, :unrecognized_authorization_request}
def supported?(url) do
~w(authorization token)a
|> Enum.map(&endpoint_for(&1, url))
|> Enum.all?(&is_binary/1)
end
defp do_generate_redirect_uri(redirect_uri, code, state) do
query =
redirect_uri
|> URI.parse()
|> Map.get(:query)
|> (&(URI.decode_query(&1 || "", %{"code" => code, "state" => state})
|> URI.encode_query())).()
redirect_uri
|> URI.parse()
|> Map.put(:query, query)
|> URI.to_string()
end
defp do_validate_request(params, expected_keys, options) do
proper_args = Map.take(params, expected_keys)
missing_keys = expected_keys -- Map.keys(params)
cond do
!Enum.empty?(missing_keys) ->
{:error, :missing_required_keys, keys: missing_keys}
!user_adapter(options).valid_user?(params["me"]) ->
{:error, :invalid_user}
true ->
{:ok, proper_args}
end
end
defp user_adapter(options) do
user_adapter_module =
options
|> Keyword.get(:adapters, [])
|> Keyword.get(:user)
case user_adapter_module do
nil -> raise "No user adapter was provided for the IndieWeb library."
_ -> user_adapter_module
end
end
end