-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 = <>, 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 = <<<
>/binary, Payload@1/binary>>, Sign( <>, Key, fun(Signed) -> Signature = gleam@bit_array:base64_url_encode(Signed, false), <<<>/binary, Signature/binary>> end ).