-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([encode/4, decode/6, decode_unsafely_without_validation/2]). -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())}. -file("src/ywt/internal/jwt.gleam", 33). ?DOC(false). -spec encode_header(ywt@algorithm:algorithm(), gleam@option:option(binary())) -> gleam@json:json(). encode_header(Alg, Kid) -> Fields = [{<<"typ"/utf8>>, gleam@json:string(<<"JWT"/utf8>>)}, {<<"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, gleam@json:object(Fields@1). -file("src/ywt/internal/jwt.gleam", 133). ?DOC(false). -spec verify_loop( list(ywt@verify_key:verify_key()), boolean(), fun((ywt@verify_key:verify_key(), fun((boolean()) -> FGW)) -> FGW), fun((boolean()) -> FGW) ) -> FGW. 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", 150). ?DOC(false). -spec split(binary()) -> {ok, {binary(), binary(), binary()}} | {error, ywt@internal@core:parse_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", 158). ?DOC(false). -spec part( binary(), gleam@dynamic@decode:decoder(FGZ), ywt@internal@core:parse_error(), fun((gleam@json:decode_error()) -> ywt@internal@core:parse_error()) ) -> {ok, FGZ} | {error, ywt@internal@core:parse_error()}. part(Part, Decoder, Encoding_error, Json_error) -> gleam@result:'try'( begin _pipe = gleam@bit_array:base64_url_decode(Part), gleam@result:replace_error(_pipe, Encoding_error) end, fun(Decoded) -> _pipe@1 = gleam@json:parse_bits(Decoded, Decoder), gleam@result:map_error(_pipe@1, Json_error) end ). -file("src/ywt/internal/jwt.gleam", 206). ?DOC(false). -spec key_matches_header(ywt@verify_key:verify_key(), header()) -> boolean(). key_matches_header(Key, Header) -> Key_alg = ywt@verify_key:algorithm(Key), case {erlang:element(3, Header), ywt@verify_key:id(Key)} of {{some, Header_kid}, {ok, Key_id}} -> (Header_kid =:= Key_id) andalso (Key_alg =:= erlang:element( 2, Header )); {{some, _}, {error, _}} -> false; {_, _} -> Key_alg =:= erlang:element(2, Header) end. -file("src/ywt/internal/jwt.gleam", 254). ?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", 234). ?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())) -> FHK) ) -> FHK. 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))) ), Payload@1 = encode_json(ywt@claim:encode(Claims, Payload)), Verify_message = <<<
>/binary, Payload@1/binary>>, Sign( <>, Key, fun(Signed) -> Signature = gleam@bit_array:base64_url_encode(Signed, false), <<<>/binary, Signature/binary>> end ). -file("src/ywt/internal/jwt.gleam", 62). ?DOC(false). -spec decode( binary(), gleam@dynamic@decode:decoder(FGO), list(ywt@claim:claim()), list(ywt@verify_key:verify_key()), fun((bitstring(), bitstring(), ywt@verify_key:verify_key(), fun((boolean()) -> FGS)) -> FGS), fun(({ok, FGO} | {error, ywt@internal@core:parse_error()}) -> FGS) ) -> FGS. 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, header_decoder(), invalid_header_encoding, fun(Field@0) -> {invalid_header_json, Field@0} end ), fun(Parsed_header) -> gleam@result:'try'( payload(Raw_payload, Decoder, Claims), fun(Parsed_payload) -> gleam@result:'try'( begin _pipe = gleam@bit_array:base64_url_decode( Raw_signature ), gleam@result:replace_error( _pipe, invalid_signature_encoding ) end, fun(Signature) -> Matching_keys = gleam@list:filter( Keys, fun(_capture) -> key_matches_header( _capture, Parsed_header ) end ), gleam@bool:guard( gleam@list:is_empty(Matching_keys), {error, no_matching_key}, fun() -> Verify_message = <>, 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, case Continuation_or_error of {ok, Continuation} -> Continuation(); {error, Error} -> Resolve({error, Error}) end. -file("src/ywt/internal/jwt.gleam", 22). ?DOC(false). -spec header_decoder() -> gleam@dynamic@decode:decoder(header()). header_decoder() -> gleam@dynamic@decode:field( <<"typ"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Typ) -> 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) -> case Typ of <<"JWT"/utf8>> -> gleam@dynamic@decode:success( {header, Alg, Kid} ); _ -> gleam@dynamic@decode:failure( {header, Alg, Kid}, <<"typ"/utf8>> ) end end ) end ) end ). -file("src/ywt/internal/jwt.gleam", 180). ?DOC(false). -spec payload( binary(), gleam@dynamic@decode:decoder(FHD), list(ywt@claim:claim()) ) -> {ok, FHD} | {error, ywt@internal@core:parse_error()}. payload(Payload, Decoder, Claims) -> gleam@result:'try'( part( Payload, {decoder, fun gleam@dynamic@decode:decode_dynamic/1}, invalid_payload_encoding, fun(Field@0) -> {invalid_payload_json, Field@0} end ), fun(Dynamic) -> gleam@result:'try'( ywt@claim:verify(Dynamic, Claims), fun(_use0) -> nil = _use0, _pipe = gleam@dynamic@decode:run(Dynamic, Decoder), gleam@result:map_error( _pipe, fun(Field@0) -> {payload_decoding_error, Field@0} end ) end ) end ). -file("src/ywt/internal/jwt.gleam", 49). ?DOC(false). -spec decode_unsafely_without_validation( binary(), gleam@dynamic@decode:decoder(FGK) ) -> {ok, FGK} | {error, nil}. decode_unsafely_without_validation(Jwt, Payload_decoder) -> Result = begin gleam@result:'try'( split(Jwt), fun(_use0) -> {_, Raw_payload, _} = _use0, payload(Raw_payload, Payload_decoder, []) end ) end, gleam@result:replace_error(Result, nil).