aboutsummaryrefslogtreecommitdiff
path: root/server/_build/default/lib/jwt/src/jwk.erl
diff options
context:
space:
mode:
authorCalvin Morrison <calvin@pobox.com>2025-09-03 21:15:36 -0400
committerCalvin Morrison <calvin@pobox.com>2025-09-03 21:15:36 -0400
commit49fa5aa2a127bdf8924d02bf77e5086b39c7a447 (patch)
tree61d86a7705dacc9fddccc29fa79d075d83ab8059 /server/_build/default/lib/jwt/src/jwk.erl
i vibe coded itHEADmaster
Diffstat (limited to 'server/_build/default/lib/jwt/src/jwk.erl')
-rw-r--r--server/_build/default/lib/jwt/src/jwk.erl69
1 files changed, 69 insertions, 0 deletions
diff --git a/server/_build/default/lib/jwt/src/jwk.erl b/server/_build/default/lib/jwt/src/jwk.erl
new file mode 100644
index 0000000..9226aae
--- /dev/null
+++ b/server/_build/default/lib/jwt/src/jwk.erl
@@ -0,0 +1,69 @@
+%% @doc RFC 7517: JSON Web Key (JWK)
+
+-module(jwk).
+-include_lib("public_key/include/OTP-PUB-KEY.hrl").
+
+-export([encode/2, decode/2]).
+
+-type id() :: binary().
+-type public_key() :: #'RSAPublicKey'{} | pem().
+-type pem() :: binary().
+-type json() :: binary().
+
+-spec encode(id(), public_key()) -> {ok, json()} | {error, _}.
+%% @doc encode Erlang/OTP Key to JWK
+encode(Id, #'RSAPublicKey'{modulus = N, publicExponent = E}) ->
+ {ok, jsx:encode(
+ #{
+ keys =>
+ [
+ #{
+ kid => Id,
+ kty => <<"RSA">>,
+ n => encode_int(N),
+ e => encode_int(E)
+ }
+ ]
+ }
+ )};
+encode(Id, PEM) when is_binary(PEM) ->
+ [RSAEntry] = public_key:pem_decode(PEM),
+ encode(Id, public_key:pem_entry_decode(RSAEntry));
+encode(_, _) ->
+ {error, not_supported}.
+
+-spec decode(id(), json()) -> {ok, public_key()} | {error, _}.
+%% @doc decode JWK to Erlang/OTP Key
+decode(Id, #{<<"keys">> := JWTs}) ->
+ decode(
+ lists:dropwhile(
+ fun(X) ->
+ maps:get(<<"kid">>, X, undefined) /= Id
+ end,
+ JWTs
+ )
+ );
+decode(Id, Json) when is_binary(Json) ->
+ decode(Id, jsx:decode(Json, [return_maps])).
+
+%% @private
+decode([#{<<"kty">> := <<"RSA">>, <<"n">> := N, <<"e">> := E} | _]) ->
+ {ok,
+ #'RSAPublicKey'{
+ modulus = decode_int(N),
+ publicExponent = decode_int(E)
+ }
+ };
+decode([]) ->
+ {error, not_found};
+decode(_) ->
+ {error, not_supported}.
+
+
+%% @private
+encode_int(X) ->
+ base64url:encode(binary:encode_unsigned(X)).
+
+%% @private
+decode_int(X) ->
+ binary:decode_unsigned(base64url:decode(X)).