Packages
indieweb
0.0.62
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/default/code.ex
defmodule IndieWeb.Auth.DefaultCodeAdapter do
@moduledoc """
Provides a implementation of the stateful parts of IndieAuth.
"""
@behaviour IndieWeb.Auth.Code.Adapter
@impl true
def generate() do
raw_code = :crypto.strong_rand_bytes(16)
:crypto.hash(:sha256, raw_code) |> Base.url_encode64(padding: false)
end
@impl true
def generate_challenge(options) do
# This should follow the logic at https://tools.ietf.org/html/rfc7636#section-4.1
verifier = :crypto.strong_rand_bytes(16) |> Base.encode32(padding: false)
method = Keyword.get(options, :method, "s256")
challenge = verification_method(method).(verifier)
{verifier, challenge, method}
end
@impl true
def persist(code, client_id, redirect_uri, args, options) do
code_age = Keyword.get(options, :code_age, 10 * 60)
IndieWeb.Cache.set(
"code:#{code}",
[client_id: client_id, redirect_uri: redirect_uri, params: args],
[expire: code_age],
options
)
end
@impl true
def persist_challenge(code, challenge, method, options) do
code_challenge_key = "pkce:for-code:#{code}"
code_age = Keyword.get(options, :code_age, 10 * 60)
IndieWeb.Cache.set(
code_challenge_key,
[challenge: challenge, challenge_method: method],
[expire: code_age],
options
)
end
@impl true
def verify(code, client_id, redirect_uri, options) do
case IndieWeb.Cache.get("code:#{code}", nil, options) do
nil ->
{:error, :code_not_found}
[client_id: ^client_id, redirect_uri: ^redirect_uri, params: params] ->
{:ok, params}
params ->
error =
cond do
params[:redirect_uri] != redirect_uri -> :mismatched_redirect_uri
params[:client_id] != client_id -> :mismatched_client_id
true -> :unexpected_error
end
{:error, error}
end
end
@impl true
def verify_challenge(code_verifier, code, options) do
code_challenge_key = "pkce:for-code:#{code}"
case IndieWeb.Cache.get(code_challenge_key, nil, options) do
nil ->
{:error, :code_challenge_not_found}
[challenge: challenge, challenge_method: method] ->
generated_challenge = verification_method(method).(code_verifier)
if generated_challenge == challenge do
:ok
else
{:error, :code_verifier_mismatch}
end
invalid_challenge ->
{:error, :invalid_code_challenge_method, arg: invalid_challenge}
end
end
defp verification_method(method) do
case String.downcase(method) do
"s256" ->
fn verifier -> Base.url_encode64(:crypto.hash(:sha256, verifier), padding: false) end
"s512" ->
fn verifier -> Base.url_encode64(:crypto.hash(:sha512, verifier), padding: false) end
"md5" ->
fn verifier -> Base.url_encode64(:crypto.hash(:md5, verifier), padding: false) end
_ ->
fn _verifier -> nil end
end
end
@impl true
def destroy(code, options \\ []) do
IndieWeb.Cache.delete("code:#{code}", options)
end
end