-module(flwr_oauth2). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/flwr_oauth2.gleam"). -export([secret_is_valid/1, parse_scope/1, parse_token_response/1, make_redirect_uri/1, to_http_request/1, random_state/1, random_state32/0]). -export_type([response_type/0, client_id/0, secret/0, state/0, authorization_code_grant_redirect_uri/0, token_request/0, client_authentication/0, error/0, access_token_response/0, auth_location/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. -type response_type() :: code | token. -type client_id() :: {client_id, binary()}. -type secret() :: {secret, binary()} | {secret_with_expiration, binary(), gleam@time@timestamp:timestamp()}. -type state() :: {state, binary()}. -type authorization_code_grant_redirect_uri() :: {authorization_code_grant_redirect_uri, gleam@uri:uri(), response_type(), gleam@option:option(gleam@uri:uri()), client_id(), list(binary()), gleam@option:option(state())} | {authorization_code_grant_redirect_uri_with_p_k_c_e, gleam@uri:uri(), response_type(), gleam@option:option(gleam@uri:uri()), client_id(), list(binary()), gleam@option:option(state()), binary()}. -type token_request() :: {authorization_code_grant_token_request, gleam@uri:uri(), client_authentication(), gleam@option:option(gleam@uri:uri()), binary()} | {authorization_code_grant_token_request_with_p_k_c_e, gleam@uri:uri(), client_authentication(), gleam@option:option(gleam@uri:uri()), binary(), binary()} | {resource_owner_credentials_grant_token_request, gleam@uri:uri(), client_authentication(), binary(), binary(), list(binary())} | {refresh_token_grant_request, gleam@uri:uri(), client_authentication(), binary(), list(binary())} | {client_credentials_grant_token_request, gleam@uri:uri(), client_authentication(), list(binary())}. -type client_authentication() :: {client_secret_basic, client_id(), secret()} | {client_secret_post, client_id(), secret()} | {public_authentication, client_id()}. -type error() :: secret_expired | invalid_uri. -type access_token_response() :: {token_error_response, integer(), binary(), gleam@option:option(binary()), gleam@option:option(binary())} | {access_token_response, binary(), binary(), gleam@option:option(integer()), gleam@option:option(binary()), list(binary())}. -type auth_location() :: {header, binary(), binary()} | {body, list({binary(), binary()})}. -file("src/flwr_oauth2.gleam", 397). -spec parse_token_error_response(gleam@http@response:response(binary())) -> {ok, access_token_response()} | {error, gleam@json:decode_error()}. parse_token_error_response(Response) -> Error_decoder = begin gleam@dynamic@decode:field( <<"error"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Error) -> gleam@dynamic@decode:optional_field( <<"error_description"/utf8>>, none, gleam@dynamic@decode:optional( {decoder, fun gleam@dynamic@decode:decode_string/1} ), fun(Error_description) -> gleam@dynamic@decode:optional_field( <<"error_uri"/utf8>>, none, gleam@dynamic@decode:optional( {decoder, fun gleam@dynamic@decode:decode_string/1} ), fun(Error_uri) -> gleam@dynamic@decode:success( {token_error_response, erlang:element(2, Response), Error, Error_description, Error_uri} ) end ) end ) end ) end, gleam@json:parse(erlang:element(4, Response), Error_decoder). -file("src/flwr_oauth2.gleam", 424). ?DOC( " Checks if a given secret is not expired.\n" " Returns always true for secrets that cannot expire.\n" ). -spec secret_is_valid(secret()) -> boolean(). secret_is_valid(Secret) -> _pipe = case Secret of {secret, _} -> false; {secret_with_expiration, _, Expires_at} -> gleam@time@timestamp:compare( Expires_at, gleam@time@timestamp:system_time() ) =:= lt end, gleam@bool:negate(_pipe). -file("src/flwr_oauth2.gleam", 319). -spec create_authentication(client_authentication()) -> {ok, auth_location()} | {error, error()}. create_authentication(Auth) -> case Auth of {client_secret_basic, Client_id, Client_secret} -> _pipe = Client_secret, _pipe@1 = secret_is_valid(_pipe), _pipe@2 = gleam@bool:negate(_pipe@1), gleam@bool:guard( _pipe@2, {error, secret_expired}, fun() -> Encoded = begin _pipe@3 = (<<<<(erlang:element(2, Client_id))/binary, ":"/utf8>>/binary, (erlang:element(2, Client_secret))/binary>>), _pipe@4 = gleam_stdlib:identity(_pipe@3), gleam_stdlib:bit_array_base64_encode(_pipe@4, false) end, _pipe@5 = {header, <<"authentication"/utf8>>, <<"Basic "/utf8, Encoded/binary>>}, {ok, _pipe@5} end ); {client_secret_post, Client_id@1, Client_secret@1} -> _pipe@6 = Client_secret@1, _pipe@7 = secret_is_valid(_pipe@6), _pipe@8 = gleam@bool:negate(_pipe@7), gleam@bool:guard( _pipe@8, {error, secret_expired}, fun() -> _pipe@9 = {body, [{<<"client_id"/utf8>>, erlang:element(2, Client_id@1)}, {<<"client_secret"/utf8>>, erlang:element(2, Client_secret@1)}]}, {ok, _pipe@9} end ); {public_authentication, Client_id@2} -> _pipe@10 = {body, [{<<"client_id"/utf8>>, erlang:element(2, Client_id@2)}]}, {ok, _pipe@10} end. -file("src/flwr_oauth2.gleam", 290). -spec setup_request( gleam@uri:uri(), list({binary(), binary()}), client_authentication() ) -> {ok, gleam@http@request:request(binary())} | {error, error()}. setup_request(Token_endpoint, Body, Client_auth) -> Req = begin _pipe = gleam@http@request:from_uri(Token_endpoint), gleam@result:map_error(_pipe, fun(_) -> invalid_uri end) end, _pipe@7 = begin gleam@result:map( Req, fun(Req@1) -> Req@2 = begin _pipe@1 = Req@1, _pipe@2 = gleam@http@request:set_method(_pipe@1, post), gleam@http@request:set_header( _pipe@2, <<"content-type"/utf8>>, <<"application/x-www-form-urlencoded"/utf8>> ) end, gleam@result:map( create_authentication(Client_auth), fun(Auth_location) -> case Auth_location of {header, Key, Value} -> _pipe@3 = Req@2, _pipe@4 = gleam@http@request:set_header( _pipe@3, Key, Value ), gleam@http@request:set_body( _pipe@4, gleam@uri:query_to_string(Body) ); {body, Values} -> _pipe@5 = lists:append(Values, Body), _pipe@6 = gleam@uri:query_to_string(_pipe@5), gleam@http@request:set_body(Req@2, _pipe@6) end end ) end ) end, gleam@result:flatten(_pipe@7). -file("src/flwr_oauth2.gleam", 466). -spec response_type_to_string(response_type()) -> binary(). response_type_to_string(Response_type) -> case Response_type of code -> <<"code"/utf8>>; token -> <<"token"/utf8>> end. -file("src/flwr_oauth2.gleam", 473). -spec string_not_empty(binary()) -> boolean(). string_not_empty(L) -> not gleam@string:is_empty(L). -file("src/flwr_oauth2.gleam", 439). ?DOC( " Parses a string containing the space separated scopes.\n" "\n" " ## Example\n" " ```gleam\n" " parse_scope(\"scope1 scope2\")\n" " ````\n" ). -spec parse_scope(binary()) -> list(binary()). parse_scope(Scope) -> _pipe = Scope, _pipe@1 = gleam@string:trim(_pipe), _pipe@2 = gleam@string:split(_pipe@1, <<" "/utf8>>), gleam@list:filter(_pipe@2, fun string_not_empty/1). -file("src/flwr_oauth2.gleam", 364). -spec parse_token_success_response(gleam@http@response:response(binary())) -> {ok, access_token_response()} | {error, gleam@json:decode_error()}. parse_token_success_response(Response) -> Token_decoder = begin gleam@dynamic@decode:field( <<"access_token"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Access_token) -> gleam@dynamic@decode:field( <<"token_type"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Token_type) -> gleam@dynamic@decode:optional_field( <<"expires_in"/utf8>>, none, gleam@dynamic@decode:optional( {decoder, fun gleam@dynamic@decode:decode_int/1} ), fun(Expires_in) -> gleam@dynamic@decode:optional_field( <<"refresh_token"/utf8>>, none, gleam@dynamic@decode:optional( {decoder, fun gleam@dynamic@decode:decode_string/1} ), fun(Refresh_token) -> gleam@dynamic@decode:optional_field( <<"scope"/utf8>>, none, gleam@dynamic@decode:optional( {decoder, fun gleam@dynamic@decode:decode_string/1} ), fun(Scope) -> Scope@1 = begin _pipe = Scope, _pipe@1 = gleam@option:map( _pipe, fun parse_scope/1 ), gleam@option:unwrap( _pipe@1, [] ) end, gleam@dynamic@decode:success( {access_token_response, Access_token, Token_type, Expires_in, Refresh_token, Scope@1} ) end ) end ) end ) end ) end ) end, gleam@json:parse(erlang:element(4, Response), Token_decoder). -file("src/flwr_oauth2.gleam", 355). ?DOC(" Parses a token response and returns the access and refresh token if valid response, otherwise the error response.\n"). -spec parse_token_response(gleam@http@response:response(binary())) -> {ok, access_token_response()} | {error, gleam@json:decode_error()}. parse_token_response(Response) -> case erlang:element(2, Response) of 200 -> parse_token_success_response(Response); _ -> parse_token_error_response(Response) end. -file("src/flwr_oauth2.gleam", 477). -spec add_if_present(list(JFH), gleam@option:option(JFH)) -> list(JFH). add_if_present(D, Value) -> _pipe = Value, _pipe@1 = gleam@option:map(_pipe, fun gleam@list:wrap/1), _pipe@2 = gleam@option:unwrap(_pipe@1, []), lists:append(_pipe@2, D). -file("src/flwr_oauth2.gleam", 279). -spec add_scope(list({binary(), binary()}), list(binary())) -> list({binary(), binary()}). add_scope(D, Scope) -> _pipe@1 = case gleam@list:is_empty(Scope) of false -> {some, {<<"scope"/utf8>>, begin _pipe = Scope, gleam@string:join(_pipe, <<" "/utf8>>) end}}; true -> none end, add_if_present(D, _pipe@1). -file("src/flwr_oauth2.gleam", 492). -spec wrap_tuple(JFN, JFO) -> {JFN, JFO}. wrap_tuple(Name, Value) -> {Name, Value}. -file("src/flwr_oauth2.gleam", 484). -spec to_redirect_uri_query(gleam@option:option(gleam@uri:uri())) -> gleam@option:option({binary(), binary()}). to_redirect_uri_query(Value) -> _pipe = Value, _pipe@1 = gleam@option:map(_pipe, fun gleam@uri:to_string/1), gleam@option:map( _pipe@1, fun(_capture) -> wrap_tuple(<<"redirect_uri"/utf8>>, _capture) end ). -file("src/flwr_oauth2.gleam", 188). ?DOC(" Creates the uri that the resource owner should be redirected too.\n"). -spec make_redirect_uri(authorization_code_grant_redirect_uri()) -> gleam@uri:uri(). make_redirect_uri(Redirect_config) -> State = begin _pipe = erlang:element(7, Redirect_config), _pipe@1 = gleam@option:map(_pipe, fun(X) -> erlang:element(2, X) end), gleam@option:map( _pipe@1, fun(_capture) -> wrap_tuple(<<"state"/utf8>>, _capture) end ) end, Redirect_uri = to_redirect_uri_query(erlang:element(4, Redirect_config)), Code_callenge = case Redirect_config of {authorization_code_grant_redirect_uri_with_p_k_c_e, _, _, _, _, _, _, Code_challenge} -> {some, {<<"code_challenge"/utf8>>, Code_challenge}}; _ -> none end, Queries = begin _pipe@2 = [{<<"response_type"/utf8>>, response_type_to_string(erlang:element(3, Redirect_config))}, {<<"client_id"/utf8>>, erlang:element(2, erlang:element(5, Redirect_config))}, {<<"scope"/utf8>>, gleam@string:join( erlang:element(6, Redirect_config), <<" "/utf8>> )}], _pipe@3 = add_if_present(_pipe@2, Redirect_uri), _pipe@4 = add_if_present(_pipe@3, State), add_if_present(_pipe@4, Code_callenge) end, _record = erlang:element(2, Redirect_config), {uri, erlang:element(2, _record), erlang:element(3, _record), erlang:element(4, _record), erlang:element(5, _record), erlang:element(6, _record), {some, gleam@uri:query_to_string(Queries)}, erlang:element(8, _record)}. -file("src/flwr_oauth2.gleam", 218). ?DOC( " Creates a http request from the given TokenRequest, but does not send.\n" " Sending the request is done by the user of the function.\n" ). -spec to_http_request(token_request()) -> {ok, gleam@http@request:request(binary())} | {error, error()}. to_http_request(Request) -> _pipe@5 = case Request of {authorization_code_grant_token_request, _, _, Redirect_uri, Code} -> Redirect_uri@1 = to_redirect_uri_query(Redirect_uri), _pipe = [{<<"grant_type"/utf8>>, <<"authorization_code"/utf8>>}, {<<"code"/utf8>>, Code}], add_if_present(_pipe, Redirect_uri@1); {authorization_code_grant_token_request_with_p_k_c_e, _, _, Redirect_uri@2, Code@1, Code_verifier} -> Redirect_uri@3 = to_redirect_uri_query(Redirect_uri@2), _pipe@1 = [{<<"grant_type"/utf8>>, <<"authorization_code"/utf8>>}, {<<"code"/utf8>>, Code@1}, {<<"code_verifier"/utf8>>, Code_verifier}], add_if_present(_pipe@1, Redirect_uri@3); {resource_owner_credentials_grant_token_request, _, _, Username, Password, Scope} -> _pipe@2 = [{<<"grant_type"/utf8>>, <<"password"/utf8>>}, {<<"username"/utf8>>, Username}, {<<"password"/utf8>>, Password}], add_scope(_pipe@2, Scope); {refresh_token_grant_request, _, _, Refresh_token, Scope@1} -> _pipe@3 = [{<<"grant_type"/utf8>>, <<"refresh_token"/utf8>>}, {<<"refresh_token"/utf8>>, Refresh_token}], add_scope(_pipe@3, Scope@1); {client_credentials_grant_token_request, _, _, Scope@2} -> _pipe@4 = [{<<"grant_type"/utf8>>, <<"client_credentials"/utf8>>}], add_scope(_pipe@4, Scope@2) end, setup_request( erlang:element(2, Request), _pipe@5, erlang:element(3, Request) ). -file("src/flwr_oauth2.gleam", 450). ?DOC( " Generates a random State with the specified length including only uppercase and lowercase letters\n" " If length <= 0 returns an empty string\n" ). -spec random_state(integer()) -> state(). random_state(Length) -> Value@1 = case begin _pipe = prng@random:fixed_size_list(prng@random:int(0, 51), Length), _pipe@1 = prng@random:to_random_yielder(_pipe), gleam@yielder:step(_pipe@1) end of {next, Value, _} -> Value; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"flwr_oauth2"/utf8>>, function => <<"random_state"/utf8>>, line => 451, value => _assert_fail, start => 15091, 'end' => 15236, pattern_start => 15102, pattern_end => 15124}) end, _pipe@2 = gleam@list:map( Value@1, fun(_capture) -> gleam@string:slice( <<"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"/utf8>>, _capture, 1 ) end ), _pipe@3 = gleam@string:join(_pipe@2, <<""/utf8>>), {state, _pipe@3}. -file("src/flwr_oauth2.gleam", 462). ?DOC(" Generates a random 32 character long State\n"). -spec random_state32() -> state(). random_state32() -> random_state(32).