Current section

Files

Jump to
ywt_core src ywt@internal@jwt.erl
Raw

src/ywt@internal@jwt.erl

-module(ywt@internal@jwt).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/ywt/internal/jwt.gleam").
-export([decode/6, decode_unsafely_without_validation/2, encode/4]).
-export_type([header/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(false).
-type header() :: {header,
ywt@algorithm:algorithm(),
gleam@option:option(binary()),
boolean()}.
-file("src/ywt/internal/jwt.gleam", 184).
?DOC(false).
-spec verify_loop(
list(ywt@verify_key:verify_key()),
boolean(),
fun((ywt@verify_key:verify_key(), fun((boolean()) -> FME)) -> FME),
fun((boolean()) -> FME)
) -> FME.
verify_loop(Keys, Result, Verify, On_result) ->
case Keys of
[] ->
On_result(Result);
[Key | Keys@1] ->
Verify(
Key,
fun(Is_valid) ->
Result@1 = Result orelse Is_valid,
verify_loop(Keys@1, Result@1, Verify, On_result)
end
)
end.
-file("src/ywt/internal/jwt.gleam", 232).
?DOC(false).
-spec key_for_header(ywt@verify_key:verify_key(), header()) -> {ok,
ywt@verify_key:verify_key()} |
{error, nil}.
key_for_header(Key, Header) ->
case {erlang:element(3, Header), ywt@verify_key:id(Key)} of
{{some, Header_kid}, {ok, Key_id}} when Header_kid =/= Key_id ->
{error, nil};
{{some, _}, {error, _}} ->
{error, nil};
{_, _} ->
ywt@verify_key:for_algorithm(Key, erlang:element(2, Header))
end.
-file("src/ywt/internal/jwt.gleam", 177).
?DOC(false).
-spec reject_unencoded_payload_header(header()) -> {ok, nil} |
{error, ywt@internal@core:error()}.
reject_unencoded_payload_header(Header) ->
case erlang:element(4, Header) of
true ->
{ok, nil};
false ->
{error, unsupported_unencoded_payload}
end.
-file("src/ywt/internal/jwt.gleam", 201).
?DOC(false).
-spec split(binary()) -> {ok, {binary(), binary(), binary()}} |
{error, ywt@internal@core:error()}.
split(Jwt) ->
case gleam@string:split(Jwt, <<"."/utf8>>) of
[Header, Payload, Signature] ->
{ok, {Header, Payload, Signature}};
_ ->
{error, malformed_token}
end.
-file("src/ywt/internal/jwt.gleam", 209).
?DOC(false).
-spec part(
binary(),
ywt@internal@core:error(),
fun((gleam@json:decode_error()) -> ywt@internal@core:error())
) -> {ok, gleam@dynamic:dynamic_()} | {error, ywt@internal@core:error()}.
part(Part, Encoding_error, Json_error) ->
gleam@result:'try'(
begin
_pipe = ywt@internal@base64url:decode(Part),
gleam@result:replace_error(_pipe, Encoding_error)
end,
fun(Decoded) ->
_pipe@1 = ywt@internal@jose_json:parse_bits(
Decoded,
{decoder, fun gleam@dynamic@decode:decode_dynamic/1}
),
gleam@result:map_error(_pipe@1, Json_error)
end
).
-file("src/ywt/internal/jwt.gleam", 167).
?DOC(false).
-spec reject_critical_header(gleam@dynamic:dynamic_()) -> {ok, nil} |
{error, ywt@internal@core:error()}.
reject_critical_header(Header) ->
Decoder = gleam@dynamic@decode:optionally_at(
[<<"crit"/utf8>>],
nil,
gleam@dynamic@decode:failure(nil, <<"crit"/utf8>>)
),
_pipe = gleam@dynamic@decode:run(Header, Decoder),
gleam@result:replace_error(_pipe, unsupported_critical_header).
-file("src/ywt/internal/jwt.gleam", 68).
?DOC(false).
-spec decode(
binary(),
gleam@dynamic@decode:decoder(FLS),
list(ywt@claim:claim()),
list(ywt@verify_key:verify_key()),
fun((bitstring(), bitstring(), ywt@verify_key:verify_key(), fun((boolean()) -> FLW)) -> FLW),
fun(({ok, FLS} | {error, ywt@internal@core:error()}) -> FLW)
) -> FLW.
decode(Jwt, Decoder, Claims, Keys, Verify, Resolve) ->
Continuation_or_error = begin
gleam@result:'try'(
split(Jwt),
fun(_use0) ->
{Raw_header, Raw_payload, Raw_signature} = _use0,
gleam@result:'try'(
part(
Raw_header,
invalid_header_encoding,
fun(Field@0) -> {invalid_header_json, Field@0} end
),
fun(Dynamic_header) ->
gleam@result:'try'(
begin
_pipe = gleam@dynamic@decode:run(
Dynamic_header,
header_decoder()
),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {header_decoding_error, Field@0} end
)
end,
fun(Parsed_header) ->
gleam@result:'try'(
reject_unencoded_payload_header(
Parsed_header
),
fun(_use0@1) ->
nil = _use0@1,
gleam@result:'try'(
reject_critical_header(
Dynamic_header
),
fun(_use0@2) ->
nil = _use0@2,
gleam@result:'try'(
part(
Raw_payload,
invalid_payload_encoding,
fun(Field@0) -> {invalid_payload_json, Field@0} end
),
fun(Dynamic_payload) ->
gleam@result:'try'(
ywt@claim:verify_with_header(
Dynamic_header,
Dynamic_payload,
Claims
),
fun(_use0@3) ->
nil = _use0@3,
gleam@result:'try'(
begin
_pipe@1 = gleam@dynamic@decode:run(
Dynamic_payload,
Decoder
),
gleam@result:map_error(
_pipe@1,
fun(Field@0) -> {payload_decoding_error, Field@0} end
)
end,
fun(
Parsed_payload
) ->
gleam@result:'try'(
begin
_pipe@2 = ywt@internal@base64url:decode(
Raw_signature
),
gleam@result:replace_error(
_pipe@2,
invalid_signature_encoding
)
end,
fun(
Signature
) ->
Matching_keys = gleam@list:filter_map(
Keys,
fun(
_capture
) ->
key_for_header(
_capture,
Parsed_header
)
end
),
gleam@bool:guard(
gleam@list:is_empty(
Matching_keys
),
{error,
no_matching_key},
fun(
) ->
Verify_message = <<Raw_header/binary,
"."/utf8,
Raw_payload/binary>>,
Verify@1 = fun(
Key,
Next
) ->
Verify(
Verify_message,
Signature,
Key,
Next
)
end,
On_result = fun(
Result
) ->
case Result of
true ->
Resolve(
{ok,
Parsed_payload}
);
false ->
Resolve(
{error,
invalid_signature}
)
end
end,
{ok,
fun(
) ->
verify_loop(
Matching_keys,
false,
Verify@1,
On_result
)
end}
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end,
case Continuation_or_error of
{ok, Continuation} ->
Continuation();
{error, Error} ->
Resolve({error, Error})
end.
-file("src/ywt/internal/jwt.gleam", 24).
?DOC(false).
-spec header_decoder() -> gleam@dynamic@decode:decoder(header()).
header_decoder() ->
gleam@dynamic@decode:field(
<<"alg"/utf8>>,
ywt@algorithm:decoder(),
fun(Alg) ->
gleam@dynamic@decode:optional_field(
<<"kid"/utf8>>,
none,
gleam@dynamic@decode:map(
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Field@0) -> {some, Field@0} end
),
fun(Kid) ->
gleam@dynamic@decode:optional_field(
<<"b64"/utf8>>,
true,
{decoder, fun gleam@dynamic@decode:decode_bool/1},
fun(B64) ->
gleam@dynamic@decode:success(
{header, Alg, Kid, B64}
)
end
)
end
)
end
).
-file("src/ywt/internal/jwt.gleam", 32).
?DOC(false).
-spec encode_header(
ywt@algorithm:algorithm(),
gleam@option:option(binary()),
list(ywt@claim:claim())
) -> gleam@json:json().
encode_header(Alg, Kid, Claims) ->
Fields = [{<<"alg"/utf8>>, ywt@algorithm:to_json(Alg)}],
Fields@1 = case Kid of
{some, Kid@1} ->
[{<<"kid"/utf8>>, gleam@json:string(Kid@1)} | Fields];
none ->
Fields
end,
ywt@claim:encode_header(Claims, Fields@1).
-file("src/ywt/internal/jwt.gleam", 51).
?DOC(false).
-spec decode_unsafely_without_validation(
binary(),
gleam@dynamic@decode:decoder(FLO)
) -> {ok, FLO} | {error, ywt@internal@core:error()}.
decode_unsafely_without_validation(Jwt, Payload_decoder) ->
gleam@result:'try'(
split(Jwt),
fun(_use0) ->
{_, Raw_payload, _} = _use0,
gleam@result:'try'(
part(
Raw_payload,
invalid_payload_encoding,
fun(Field@0) -> {invalid_payload_json, Field@0} end
),
fun(Dynamic_payload) ->
_pipe = gleam@dynamic@decode:run(
Dynamic_payload,
Payload_decoder
),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {payload_decoding_error, Field@0} end
)
end
)
end
).
-file("src/ywt/internal/jwt.gleam", 277).
?DOC(false).
-spec encode_json(gleam@json:json()) -> binary().
encode_json(Part) ->
gleam@bit_array:base64_url_encode(
<<(gleam@json:to_string(Part))/binary>>,
false
).
-file("src/ywt/internal/jwt.gleam", 257).
?DOC(false).
-spec encode(
list({binary(), gleam@json:json()}),
list(ywt@claim:claim()),
ywt@sign_key:sign_key(),
fun((bitstring(), ywt@sign_key:sign_key(), fun((bitstring()) -> binary())) -> FMN)
) -> FMN.
encode(Payload, Claims, Key, Sign) ->
Alg = ywt@sign_key:algorithm(Key),
Header = encode_json(
encode_header(
Alg,
gleam@option:from_result(ywt@sign_key:id(Key)),
Claims
)
),
Payload@1 = encode_json(ywt@claim:encode(Claims, Payload)),
Verify_message = <<<<Header/binary, "."/utf8>>/binary, Payload@1/binary>>,
Sign(
<<Verify_message/binary>>,
Key,
fun(Signed) ->
Signature = gleam@bit_array:base64_url_encode(Signed, false),
<<<<Verify_message/binary, "."/utf8>>/binary, Signature/binary>>
end
).