-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", 29). ?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", 165). ?DOC(false). -spec verify_loop( list(ywt@verify_key:verify_key()), boolean(), fun((ywt@verify_key:verify_key(), fun((boolean()) -> FDF)) -> FDF), fun((boolean()) -> FDF) ) -> FDF. 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", 182). ?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", 211). ?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", 259). ?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", 239). ?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())) -> FDM) ) -> FDM. 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 = <<<
>/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", 69). ?DOC(false). -spec decode( binary(), gleam@dynamic@decode:decoder(FCX), list(ywt@claim:claim()), list(ywt@verify_key:verify_key()), fun((bitstring(), bitstring(), ywt@verify_key:verify_key(), fun((boolean()) -> FDB)) -> FDB), fun(({ok, FCX} | {error, ywt@internal@core:parse_error()}) -> FDB) ) -> FDB. 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(Error) -> {invalid_header_json, {unable_to_decode, Error}} end ) end, fun(Parsed_header) -> 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@1) -> nil = _use0@1, 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 = gleam@bit_array:base64_url_decode( Raw_signature ), gleam@result:replace_error( _pipe@2, 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 ) end ) end ) end, case Continuation_or_error of {ok, Continuation} -> Continuation(); {error, Error@1} -> Resolve({error, Error@1}) end. -file("src/ywt/internal/jwt.gleam", 190). ?DOC(false). -spec part( binary(), ywt@internal@core:parse_error(), fun((gleam@json:decode_error()) -> ywt@internal@core:parse_error()) ) -> {ok, gleam@dynamic:dynamic_()} | {error, ywt@internal@core:parse_error()}. part(Part, 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, fun gleam@dynamic@decode:decode_dynamic/1} ), gleam@result:map_error(_pipe@1, Json_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( <<"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:success({header, Alg, Kid}) end ) end ). -file("src/ywt/internal/jwt.gleam", 48). ?DOC(false). -spec decode_unsafely_without_validation( binary(), gleam@dynamic@decode:decoder(FCT) ) -> {ok, FCT} | {error, nil}. decode_unsafely_without_validation(Jwt, Payload_decoder) -> Result = begin 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 ) end, gleam@result:replace_error(Result, nil).