Packages

A small, focused OAuth 2.0 library for Gleam, built on top of Gleams HTTP requests and responses.

Current section

Files

Jump to
flwr_oauth2 src flwr_oauth2@authorization_grant.erl
Raw

src/flwr_oauth2@authorization_grant.erl

-module(flwr_oauth2@authorization_grant).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/flwr_oauth2/authorization_grant.gleam").
-export([to_string/1, error_to_string/1, new/0, set_authorization_endpoint/2, set_response_type/2, set_redirect_uri/2, set_client_id/2, set_scope/2, set_state/2, set_code_challenge/3, parse_authorization_response_query/1, parse_authorization_response/1, to_token_request/4, to_token_request_with_pkce_verifier/5, make_redirect_uri/1, random_state/1, random_state32/0]).
-export_type([state/0, response_type/0, code_challenge_method/0, authorization_code_grant_request/0, authorization_response/0, parse_error/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 state() :: {state, binary()}.
-type response_type() :: code | token.
-type code_challenge_method() :: plain | s256.
-type authorization_code_grant_request() :: {authorization_code_grant_request,
gleam@uri:uri(),
response_type(),
gleam@option:option(gleam@uri:uri()),
flwr_oauth2:client_id(),
list(binary()),
gleam@option:option(state()),
gleam@option:option(binary()),
gleam@option:option(code_challenge_method())}.
-type authorization_response() :: {implicit_grant_response,
gleam@option:option(state()),
flwr_oauth2:access_token_response()} |
{code_grant_response, gleam@option:option(state()), binary()} |
{error_response,
gleam@option:option(state()),
binary(),
gleam@option:option(binary()),
gleam@option:option(gleam@uri:uri())}.
-type parse_error() :: {parse_error, gleam@option:option(binary())}.
-file("src/flwr_oauth2/authorization_grant.gleam", 73).
?DOC(" Creates a String representation of the AuthorizationResponse\n").
-spec to_string(authorization_response()) -> binary().
to_string(Resp) ->
State = begin
_pipe = erlang:element(2, Resp),
_pipe@1 = gleam@option:map(_pipe, fun(S) -> erlang:element(2, S) end),
gleam@option:unwrap(_pipe@1, <<"None"/utf8>>)
end,
case Resp of
{implicit_grant_response, _, Token} ->
Token_response = flwr_oauth2:to_string_token_response(Token),
<<<<<<<<"ImplicitGrantResponse(
token: "/utf8,
Token_response/binary>>/binary,
",
state: "/utf8>>/binary,
State/binary>>/binary,
"
)"/utf8>>;
{code_grant_response, _, Code} ->
<<<<<<<<"CodeGrantResponse(code: "/utf8, Code/binary>>/binary,
", state: "/utf8>>/binary,
State/binary>>/binary,
")"/utf8>>;
{error_response, _, Error, Error_description, Error_uri} ->
Error_description@1 = begin
_pipe@2 = Error_description,
gleam@option:unwrap(_pipe@2, <<"None"/utf8>>)
end,
Error_uri@1 = begin
_pipe@3 = Error_uri,
_pipe@4 = gleam@option:map(_pipe@3, fun gleam@uri:to_string/1),
gleam@option:unwrap(_pipe@4, <<"None"/utf8>>)
end,
<<<<<<<<<<<<<<<<"ErrorResponse(
error: "/utf8,
Error/binary>>/binary,
",
error_description: "/utf8>>/binary,
Error_description@1/binary>>/binary,
",
error_uri: "/utf8>>/binary,
Error_uri@1/binary>>/binary,
",
state: "/utf8>>/binary,
State/binary>>/binary,
"
)"/utf8>>
end.
-file("src/flwr_oauth2/authorization_grant.gleam", 103).
-spec error_to_string(parse_error()) -> binary().
error_to_string(Error) ->
Field = begin
_pipe = erlang:element(2, Error),
gleam@option:unwrap(_pipe, <<"None"/utf8>>)
end,
<<<<"ParseError(field: "/utf8, Field/binary>>/binary, ")"/utf8>>.
-file("src/flwr_oauth2/authorization_grant.gleam", 110).
?DOC(
" Creates a new Authorization Code Grant Request, with empty fields.\n"
" Mostly a convenience function, watch out for empty values!\n"
).
-spec new() -> authorization_code_grant_request().
new() ->
{authorization_code_grant_request,
{uri, none, none, none, none, <<""/utf8>>, none, none},
token,
none,
{client_id, <<""/utf8>>},
[],
none,
none,
none}.
-file("src/flwr_oauth2/authorization_grant.gleam", 123).
-spec set_authorization_endpoint(
authorization_code_grant_request(),
gleam@uri:uri()
) -> authorization_code_grant_request().
set_authorization_endpoint(Req, Authorization_endpoint) ->
{authorization_code_grant_request,
Authorization_endpoint,
erlang:element(3, Req),
erlang:element(4, Req),
erlang:element(5, Req),
erlang:element(6, Req),
erlang:element(7, Req),
erlang:element(8, Req),
erlang:element(9, Req)}.
-file("src/flwr_oauth2/authorization_grant.gleam", 130).
-spec set_response_type(authorization_code_grant_request(), response_type()) -> authorization_code_grant_request().
set_response_type(Req, Response_type) ->
{authorization_code_grant_request,
erlang:element(2, Req),
Response_type,
erlang:element(4, Req),
erlang:element(5, Req),
erlang:element(6, Req),
erlang:element(7, Req),
erlang:element(8, Req),
erlang:element(9, Req)}.
-file("src/flwr_oauth2/authorization_grant.gleam", 137).
-spec set_redirect_uri(authorization_code_grant_request(), gleam@uri:uri()) -> authorization_code_grant_request().
set_redirect_uri(Req, Redirect_uri) ->
{authorization_code_grant_request,
erlang:element(2, Req),
erlang:element(3, Req),
{some, Redirect_uri},
erlang:element(5, Req),
erlang:element(6, Req),
erlang:element(7, Req),
erlang:element(8, Req),
erlang:element(9, Req)}.
-file("src/flwr_oauth2/authorization_grant.gleam", 144).
-spec set_client_id(authorization_code_grant_request(), flwr_oauth2:client_id()) -> authorization_code_grant_request().
set_client_id(Req, Client_id) ->
{authorization_code_grant_request,
erlang:element(2, Req),
erlang:element(3, Req),
erlang:element(4, Req),
Client_id,
erlang:element(6, Req),
erlang:element(7, Req),
erlang:element(8, Req),
erlang:element(9, Req)}.
-file("src/flwr_oauth2/authorization_grant.gleam", 148).
-spec set_scope(authorization_code_grant_request(), list(binary())) -> authorization_code_grant_request().
set_scope(Req, Scope) ->
{authorization_code_grant_request,
erlang:element(2, Req),
erlang:element(3, Req),
erlang:element(4, Req),
erlang:element(5, Req),
Scope,
erlang:element(7, Req),
erlang:element(8, Req),
erlang:element(9, Req)}.
-file("src/flwr_oauth2/authorization_grant.gleam", 152).
-spec set_state(authorization_code_grant_request(), state()) -> authorization_code_grant_request().
set_state(Req, State) ->
{authorization_code_grant_request,
erlang:element(2, Req),
erlang:element(3, Req),
erlang:element(4, Req),
erlang:element(5, Req),
erlang:element(6, Req),
{some, State},
erlang:element(8, Req),
erlang:element(9, Req)}.
-file("src/flwr_oauth2/authorization_grant.gleam", 156).
-spec set_code_challenge(
authorization_code_grant_request(),
binary(),
code_challenge_method()
) -> authorization_code_grant_request().
set_code_challenge(Req, Code_challenge, Code_challenge_method) ->
{authorization_code_grant_request,
erlang:element(2, Req),
erlang:element(3, Req),
erlang:element(4, Req),
erlang:element(5, Req),
erlang:element(6, Req),
erlang:element(7, Req),
{some, Code_challenge},
{some, Code_challenge_method}}.
-file("src/flwr_oauth2/authorization_grant.gleam", 334).
-spec parse_optional_field(gleam@dict:dict(binary(), FQI), binary()) -> gleam@option:option(FQI).
parse_optional_field(Query, Key) ->
_pipe = Query,
_pipe@1 = gleam_stdlib:map_get(_pipe, Key),
gleam@option:from_result(_pipe@1).
-file("src/flwr_oauth2/authorization_grant.gleam", 343).
-spec create_parse_error({ok, FQM} | {error, any()}, binary()) -> {ok, FQM} |
{error, parse_error()}.
create_parse_error(Res, Field) ->
_pipe = Res,
gleam@result:replace_error(_pipe, {parse_error, {some, Field}}).
-file("src/flwr_oauth2/authorization_grant.gleam", 307).
-spec parse_optional_uri(
gleam@dict:dict(binary(), binary()),
binary(),
fun((binary()) -> {ok, FPT} | {error, any()}),
fun((gleam@option:option(FPT)) -> FPY)
) -> {ok, FPY} | {error, parse_error()}.
parse_optional_uri(Query, Key, Parser, Apply) ->
case gleam_stdlib:map_get(Query, Key) of
{error, _} ->
{ok, Apply(none)};
{ok, Value} ->
Value@1 = begin
_pipe = Parser(Value),
create_parse_error(_pipe, Key)
end,
gleam@result:'try'(
Value@1,
fun(Parsed) -> {ok, Apply({some, Parsed})} end
)
end.
-file("src/flwr_oauth2/authorization_grant.gleam", 290).
-spec parse_error_response(gleam@dict:dict(binary(), binary())) -> {ok,
gleam@option:option(authorization_response())} |
{error, parse_error()}.
parse_error_response(Query) ->
case gleam_stdlib:map_get(Query, <<"error"/utf8>>) of
{ok, Error} ->
Error_description = begin
_pipe = Query,
parse_optional_field(_pipe, <<"error_description"/utf8>>)
end,
State = begin
_pipe@1 = Query,
_pipe@2 = parse_optional_field(_pipe@1, <<"state"/utf8>>),
gleam@option:map(_pipe@2, fun(Field@0) -> {state, Field@0} end)
end,
parse_optional_uri(
Query,
<<"error_uri"/utf8>>,
fun gleam_stdlib:uri_parse/1,
fun(Error_uri) ->
{some,
{error_response,
State,
Error,
Error_description,
Error_uri}}
end
);
{error, _} ->
{ok, none}
end.
-file("src/flwr_oauth2/authorization_grant.gleam", 323).
-spec parse_required_field(
gleam@dict:dict(binary(), FQB),
binary(),
fun((FQB) -> {ok, FQE} | {error, parse_error()})
) -> {ok, FQE} | {error, parse_error()}.
parse_required_field(Query, Key, Then) ->
_pipe = Query,
_pipe@1 = gleam_stdlib:map_get(_pipe, Key),
_pipe@2 = create_parse_error(_pipe@1, Key),
gleam@result:'try'(_pipe@2, Then).
-file("src/flwr_oauth2/authorization_grant.gleam", 242).
-spec parse_authorization_code_response(gleam@dict:dict(binary(), binary())) -> {ok,
authorization_response()} |
{error, parse_error()}.
parse_authorization_code_response(Query) ->
parse_required_field(
Query,
<<"code"/utf8>>,
fun(Code) ->
State = begin
_pipe = Query,
_pipe@1 = parse_optional_field(_pipe, <<"state"/utf8>>),
gleam@option:map(_pipe@1, fun(Field@0) -> {state, Field@0} end)
end,
{ok, {code_grant_response, State, Code}}
end
).
-file("src/flwr_oauth2/authorization_grant.gleam", 253).
-spec parse_authorization_implicit_response(gleam@dict:dict(binary(), binary())) -> {ok,
authorization_response()} |
{error, parse_error()}.
parse_authorization_implicit_response(Query) ->
parse_required_field(
Query,
<<"access_token"/utf8>>,
fun(Access_token) ->
parse_required_field(
Query,
<<"token_type"/utf8>>,
fun(Token_type) ->
Expires_in = begin
_pipe = Query,
_pipe@1 = parse_optional_field(
_pipe,
<<"expires_in"/utf8>>
),
_pipe@2 = gleam@option:map(
_pipe@1,
fun gleam_stdlib:parse_int/1
),
_pipe@3 = gleam@option:map(
_pipe@2,
fun(Res) ->
gleam@result:map(
Res,
fun(Field@0) -> {some, Field@0} end
)
end
),
_pipe@4 = gleam@option:unwrap(_pipe@3, {ok, none}),
create_parse_error(_pipe@4, <<"expires_in"/utf8>>)
end,
gleam@result:'try'(
Expires_in,
fun(Expires_in@1) ->
Scope = begin
_pipe@5 = Query,
_pipe@6 = parse_optional_field(
_pipe@5,
<<"scope"/utf8>>
),
_pipe@7 = gleam@option:map(
_pipe@6,
fun flwr_oauth2:parse_scope/1
),
gleam@option:unwrap(_pipe@7, [])
end,
State = begin
_pipe@8 = Query,
_pipe@9 = gleam_stdlib:map_get(
_pipe@8,
<<"state"/utf8>>
),
_pipe@10 = gleam@option:from_result(_pipe@9),
gleam@option:map(
_pipe@10,
fun(Field@0) -> {state, Field@0} end
)
end,
{ok,
{implicit_grant_response,
State,
{access_token_response,
Access_token,
Token_type,
Expires_in@1,
none,
Scope}}}
end
)
end
)
end
).
-file("src/flwr_oauth2/authorization_grant.gleam", 229).
?DOC(
" Parses the query from the authorization response HTTP request.\n"
" This function is intended to be used in case your implementation does not use the standard gleam/http/request type.\n"
).
-spec parse_authorization_response_query(gleam@dict:dict(binary(), binary())) -> {ok,
authorization_response()} |
{error, parse_error()}.
parse_authorization_response_query(Query) ->
Error_response = parse_error_response(Query),
gleam@result:'try'(
Error_response,
fun(Error_response@1) ->
Error_response@2 = begin
_pipe = Error_response@1,
gleam@option:map(_pipe, fun(Field@0) -> {ok, Field@0} end)
end,
gleam@option:lazy_unwrap(
Error_response@2,
fun() ->
Code_response = parse_authorization_code_response(Query),
gleam@result:try_recover(
Code_response,
fun(_) ->
parse_authorization_implicit_response(Query)
end
)
end
)
end
).
-file("src/flwr_oauth2/authorization_grant.gleam", 206).
?DOC(
" Parses the redirect URI, with the code for the code grant type or the access token for the implicit grant type.\n"
" See [Section 4.1.2](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2)\n"
).
-spec parse_authorization_response(gleam@http@request:request(any())) -> {ok,
authorization_response()} |
{error, parse_error()}.
parse_authorization_response(Req) ->
Query = begin
_pipe = erlang:element(9, Req),
_pipe@1 = gleam@option:map(_pipe, fun gleam_stdlib:parse_query/1),
_pipe@2 = gleam@option:to_result(_pipe@1, nil),
_pipe@3 = gleam@result:flatten(_pipe@2),
_pipe@4 = gleam@result:map_error(
_pipe@3,
fun(_) -> {parse_error, none} end
),
_pipe@5 = gleam@result:map(
_pipe@4,
fun(D) -> case gleam@list:is_empty(D) of
true ->
{error, {parse_error, none}};
false ->
{ok, D}
end end
),
gleam@result:flatten(_pipe@5)
end,
gleam@result:'try'(
Query,
fun(Pairs) ->
Query@1 = begin
_pipe@6 = Pairs,
maps:from_list(_pipe@6)
end,
parse_authorization_response_query(Query@1)
end
).
-file("src/flwr_oauth2/authorization_grant.gleam", 349).
?DOC(
" Utility function to create a TokenRequest from an AuthorizationResponse.\n"
" If the AuthorizationResponse is an ErrorResponse or an ImplicitGrantResponse returns Error(Nil), otherwise returns an AuthorizationCodeGrantTokenRequest.\n"
).
-spec to_token_request(
authorization_response(),
gleam@uri:uri(),
flwr_oauth2:client_authentication(),
gleam@option:option(gleam@uri:uri())
) -> {ok, flwr_oauth2:token_request()} | {error, nil}.
to_token_request(
Authorization_response,
Token_endpoint,
Authentication,
Redirect_uri
) ->
case Authorization_response of
{code_grant_response, _, Code} ->
_pipe = {authorization_code_grant_token_request,
Token_endpoint,
Authentication,
Redirect_uri,
Code},
{ok, _pipe};
_ ->
{error, nil}
end.
-file("src/flwr_oauth2/authorization_grant.gleam", 371).
?DOC(
" Utility function to create a TokenRequest from an AuthorizationResponse.\n"
" If the AuthorizationResponse is an ErrorResponse or an ImplicitGrantResponse returns Error(Nil), otherwise returns an AuthorizationCodeGrantTokenRequest.\n"
).
-spec to_token_request_with_pkce_verifier(
authorization_response(),
gleam@uri:uri(),
flwr_oauth2:client_authentication(),
gleam@option:option(gleam@uri:uri()),
flwr_oauth2@pkce:verifier()
) -> {ok, flwr_oauth2:token_request()} | {error, nil}.
to_token_request_with_pkce_verifier(
Authorization_response,
Token_endpoint,
Authentication,
Redirect_uri,
Verifier
) ->
case Authorization_response of
{code_grant_response, _, Code} ->
_pipe = {authorization_code_grant_token_request_with_p_k_c_e,
Token_endpoint,
Authentication,
Redirect_uri,
Code,
erlang:element(2, Verifier)},
{ok, _pipe};
_ ->
{error, nil}
end.
-file("src/flwr_oauth2/authorization_grant.gleam", 407).
-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/authorization_grant.gleam", 169).
?DOC(" Creates the uri that the resource owner should be redirected too.\n").
-spec make_redirect_uri(authorization_code_grant_request()) -> 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) ->
flwr_oauth2@helpers:wrap_tuple(<<"state"/utf8>>, _capture)
end
)
end,
Redirect_uri = flwr_oauth2@helpers:encode_redirect_uri(
erlang:element(4, Redirect_config)
),
Code_challenge = begin
_pipe@2 = erlang:element(8, Redirect_config),
gleam@option:map(
_pipe@2,
fun(_capture@1) ->
flwr_oauth2@helpers:wrap_tuple(
<<"code_challenge"/utf8>>,
_capture@1
)
end
)
end,
Code_challenge_method = begin
_pipe@3 = erlang:element(9, Redirect_config),
gleam@option:map(
_pipe@3,
fun(X@1) -> {<<"code_challenge_method"/utf8>>, case X@1 of
plain ->
<<"plain"/utf8>>;
s256 ->
<<"S256"/utf8>>
end} end
)
end,
Queries = begin
_pipe@4 = [{<<"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@5 = flwr_oauth2@helpers:add_if_present(_pipe@4, Redirect_uri),
_pipe@6 = flwr_oauth2@helpers:add_if_present(_pipe@5, State),
_pipe@7 = flwr_oauth2@helpers:add_if_present(
_pipe@6,
Code_challenge_method
),
flwr_oauth2@helpers:add_if_present(_pipe@7, Code_challenge)
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/authorization_grant.gleam", 397).
?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) ->
_pipe = flwr_oauth2@helpers:generate_random_string(
<<"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"/utf8>>,
Length
),
{state, _pipe}.
-file("src/flwr_oauth2/authorization_grant.gleam", 403).
?DOC(" Generates a random 32 character long State\n").
-spec random_state32() -> state().
random_state32() ->
random_state(32).