jwt stuff works now
This commit is contained in:
parent
c6ef4a5dd3
commit
f0e1a7d0e5
|
@ -1,16 +1,24 @@
|
||||||
defmodule BallsPDS.JWT do
|
defmodule BallsPDS.JWT do
|
||||||
alias BallsPDS.WAC
|
alias BallsPDS.WAC
|
||||||
require Logger
|
require Logger
|
||||||
|
use Joken.Config
|
||||||
|
|
||||||
def generate_my_jwk() do
|
def generate_my_jwk() do
|
||||||
raw_private_key = Base.decode16!(Application.get_env(:balls_pds, :owner_private_key))
|
raw_private_key =
|
||||||
|
Base.decode16!(Application.get_env(:balls_pds, :owner_private_key), case: :lower)
|
||||||
|
|
||||||
generate_jwk(raw_private_key)
|
generate_jwk(raw_private_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_jwk(raw_private_key) when is_binary(raw_private_key) do
|
def generate_jwk(raw_private_key) when is_binary(raw_private_key) do
|
||||||
private_key = raw_private_key |> Base.encode64()
|
public_key = :crypto.generate_key(:eddsa, :ed25519, raw_private_key) |> elem(0)
|
||||||
{:ok, public_key} = :crypto.generate_key(:eddsa, :ed25519, private_key)
|
|
||||||
JOSE.JWK.from_key({:okp, :Ed25519, public_key, private_key})
|
%{
|
||||||
|
"kty" => "OKP",
|
||||||
|
"crv" => "Ed25519",
|
||||||
|
"d" => Base.url_encode64(raw_private_key, padding: false),
|
||||||
|
"x" => Base.url_encode64(public_key, padding: false)
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def query_public_jwk(ap_id, key_id) do
|
def query_public_jwk(ap_id, key_id) do
|
||||||
|
@ -30,9 +38,23 @@ defmodule BallsPDS.JWT do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_kid(jwt) when is_binary(jwt) do
|
||||||
|
with {:kid, %{fields: %{"kid" => kid}}} <- {:kid, JOSE.JWT.peek_protected(jwt)} do
|
||||||
|
kid
|
||||||
|
else
|
||||||
|
{:kid, protected} ->
|
||||||
|
Logger.debug("protected: #{inspect(protected)}")
|
||||||
|
nil
|
||||||
|
|
||||||
|
error ->
|
||||||
|
Logger.error(inspect(error))
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def extract_key_info(jwt) when is_binary(jwt) do
|
def extract_key_info(jwt) when is_binary(jwt) do
|
||||||
with {:subject, {:ok, %{"sub" => subject}}} <- {:subject, JOSE.JWT.peek_payload(jwt)},
|
with {:subject, {:ok, %{"sub" => subject}}} <- {:subject, JOSE.JWT.peek_payload(jwt)},
|
||||||
{:kid, {:ok, %{"kid" => kid}}} <- {:kid, JOSE.JWT.peek_protected(jwt)} do
|
{:kid, kid} <- {:kid, get_kid(jwt)} do
|
||||||
{:ok, %{subject: subject, id: kid}}
|
{:ok, %{subject: subject, id: kid}}
|
||||||
else
|
else
|
||||||
{err, {:error, error}} ->
|
{err, {:error, error}} ->
|
||||||
|
@ -41,28 +63,37 @@ defmodule BallsPDS.JWT do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_jwt(days \\ 30) when is_integer(days) and days > 0 do
|
def generate_jwt(jwk, days) when is_integer(days) and days > 0 do
|
||||||
jwk = generate_my_jwk()
|
Logger.debug("Creating signer.")
|
||||||
signer = Joken.Signer.create("EdDSA", jwk)
|
signer = Joken.Signer.create("EdDSA", jwk, %{"kid" => "key-1"})
|
||||||
|
|
||||||
id = Application.get_env(:balls_pds, :owner_ap_id)
|
id = Application.get_env(:balls_pds, :owner_ap_id)
|
||||||
|
|
||||||
claims = %{
|
claims =
|
||||||
"iss" => id,
|
default_claims(%{
|
||||||
"sub" => id,
|
|
||||||
"aud" => Application.get_env(:balls_pds, :owner_ap_id),
|
"aud" => Application.get_env(:balls_pds, :owner_ap_id),
|
||||||
"iat" => DateTime.utc_now() |> DateTime.to_unix(),
|
"iat" => DateTime.utc_now() |> DateTime.to_unix(),
|
||||||
"exp" => DateTime.utc_now() |> DateTime.add(30, :day) |> DateTime.to_unix()
|
"exp" => DateTime.utc_now() |> DateTime.add(30, :day) |> DateTime.to_unix()
|
||||||
}
|
})
|
||||||
|
|
||||||
Joken.generate_and_sign!(claims, signer)
|
additional_claims = %{"sub" => id}
|
||||||
|
|
||||||
|
Logger.debug("Signing with claims: #{inspect(claims)}")
|
||||||
|
Joken.generate_and_sign!(claims, additional_claims, signer)
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_jwt(days) when is_integer(days) and days > 0 do
|
||||||
|
jwk = generate_my_jwk()
|
||||||
|
generate_jwt(jwk, days)
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_jwt(jwt, jwk) do
|
def verify_jwt(jwt, jwk) do
|
||||||
public_jwk = Map.drop(jwk, ["d"])
|
public_jwk = Map.drop(jwk, ["d"])
|
||||||
signer = Joken.Signer.create("EdDSA", public_jwk)
|
signer = Joken.Signer.create("EdDSA", public_jwk)
|
||||||
|
|
||||||
case Joken.verify_and_validate(signer, jwt) do
|
Logger.debug("KID: #{get_kid(jwt)}")
|
||||||
|
|
||||||
|
case Joken.verify_and_validate(public_jwk, jwt, signer) do
|
||||||
{:ok, claims} -> {:ok, claims}
|
{:ok, claims} -> {:ok, claims}
|
||||||
{:error, reason} -> {:error, reason}
|
{:error, reason} -> {:error, reason}
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,7 +24,7 @@ defmodule BallsPDS.WAC do
|
||||||
end
|
end
|
||||||
|
|
||||||
def cached_query_public_key("did:web:" <> _ = did, id) do
|
def cached_query_public_key("did:web:" <> _ = did, id) do
|
||||||
cache_key = "KEY:#{did}"
|
cache_key = "KEY:#{did}:#{id}"
|
||||||
|
|
||||||
case Cachex.get(:balls_cache, cache_key) do
|
case Cachex.get(:balls_cache, cache_key) do
|
||||||
key when is_binary(key) ->
|
key when is_binary(key) ->
|
||||||
|
@ -43,7 +43,7 @@ defmodule BallsPDS.WAC do
|
||||||
end
|
end
|
||||||
|
|
||||||
def cached_query_public_key(url, id) when is_binary(url) do
|
def cached_query_public_key(url, id) when is_binary(url) do
|
||||||
cache_key = "KEY:#{url}"
|
cache_key = "KEY:#{url}:#{id}"
|
||||||
|
|
||||||
case Cachex.get(:balls_cache, cache_key) do
|
case Cachex.get(:balls_cache, cache_key) do
|
||||||
key when is_binary(key) ->
|
key when is_binary(key) ->
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
defmodule Mix.Tasks.GenerateToken do
|
||||||
|
use Mix.Task
|
||||||
|
|
||||||
|
def run(_) do
|
||||||
|
jwk = BallsPDS.JWT.generate_my_jwk()
|
||||||
|
IO.puts("JWK: #{inspect(jwk)}")
|
||||||
|
|
||||||
|
jwt = BallsPDS.JWT.generate_jwt(jwk, 30)
|
||||||
|
IO.puts("JWT: #{jwt}")
|
||||||
|
|
||||||
|
result = BallsPDS.JWT.verify_jwt(jwt, jwk)
|
||||||
|
IO.puts("Validated: #{inspect(result)}")
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue