Packages

Basic HTTP Authentication Scheme for Wisp

Current section

Files

Jump to
wisp_basic_auth src wisp_basic_auth.erl
Raw

src/wisp_basic_auth.erl

-module(wisp_basic_auth).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/wisp_basic_auth.gleam").
-export([parse_credentials/1, validate_basic_auth/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-file("src/wisp_basic_auth.gleam", 63).
?DOC(
" Parse a list of credentials in the format `client:password`\n"
" separated by semi-colons.\n"
" \n"
" ```\n"
" parse_credentials(\"a:A;b:B\")\n"
" // -> [#(\"a\", \"A\"), #(\"b\", \"B\")]\n"
" ```\n"
).
-spec parse_credentials(binary()) -> list({binary(), binary()}).
parse_credentials(Credentials) ->
Split_credential = fun(Credential) ->
case gleam@string:split_once(Credential, <<":"/utf8>>) of
{ok, Credential@1} ->
[Credential@1];
{error, _} ->
[]
end
end,
_pipe = Credentials,
_pipe@1 = gleam@string:split(_pipe, <<";"/utf8>>),
gleam@list:flat_map(_pipe@1, Split_credential).
-file("src/wisp_basic_auth.gleam", 82).
-spec encode_creds(binary(), binary()) -> binary().
encode_creds(Id, Secret) ->
Expected_creds = <<<<Id/binary, ":"/utf8>>/binary, Secret/binary>>,
Encoded_creds = gleam_stdlib:base64_encode(
gleam_stdlib:identity(Expected_creds),
true
),
<<"Basic "/utf8, Encoded_creds/binary>>.
-file("src/wisp_basic_auth.gleam", 76).
-spec precalculate_credentials(list({binary(), binary()})) -> list(binary()).
precalculate_credentials(Known_clients) ->
gleam@list:map(
Known_clients,
fun(Id_secret) ->
encode_creds(
erlang:element(1, Id_secret),
erlang:element(2, Id_secret)
)
end
).
-file("src/wisp_basic_auth.gleam", 89).
-spec unauthorized_response(binary()) -> gleam@http@response:response(wisp:body()).
unauthorized_response(Realm) ->
Realm@1 = <<<<"Basic realm=\""/utf8, Realm/binary>>/binary, "\""/utf8>>,
_pipe = 401,
_pipe@1 = wisp:response(_pipe),
_pipe@2 = wisp:string_body(_pipe@1, <<"Unauthorized"/utf8>>),
fun gleam@http@response:set_header/3(
_pipe@2,
<<"WWW-Authenticate"/utf8>>,
Realm@1
).
-file("src/wisp_basic_auth.gleam", 97).
-spec forbidden_response() -> gleam@http@response:response(wisp:body()).
forbidden_response() ->
_pipe = 403,
_pipe@1 = wisp:response(_pipe),
wisp:string_body(_pipe@1, <<"Forbidden"/utf8>>).
-file("src/wisp_basic_auth.gleam", 37).
?DOC(
" Middleware that validates an `Authorization: Basic` header\n"
" against a known list of client ids and passwords within a realm.\n"
" \n"
" The basic authentication scheme is based on the model that the \n"
" user agent must authenticate itself with a user-ID and a password \n"
" for each realm.\n"
" \n"
" The realm value should be considered an opaque string which can \n"
" only be compared for equality with other realms on that server.\n"
" \n"
" Example header: `Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==`\n"
" \n"
" Using `curl` to set the header:\n"
" \n"
" ```bash\n"
" curl -X POST -u \"client:password\" ... https://example.com\n"
" ```\n"
" \n"
" Set the middleware in your router:\n"
" \n"
" ```gleam\n"
" let validate_basic_auth = wisp_basic_auth.validate_basic_auth(realm, known_clients)\n"
" use _ <- validate_basic_auth(request)\n"
" ```\n"
).
-spec validate_basic_auth(binary(), list({binary(), binary()})) -> fun((gleam@http@request:request(wisp@internal:connection()), fun((gleam@http@request:request(wisp@internal:connection())) -> gleam@http@response:response(wisp:body()))) -> gleam@http@response:response(wisp:body())).
validate_basic_auth(Realm, Known_clients) ->
Authorized_clients = precalculate_credentials(Known_clients),
fun(Request, Handler) ->
case gleam@http@request:get_header(Request, <<"Authorization"/utf8>>) of
{error, _} ->
unauthorized_response(Realm);
{ok, Auth_header} ->
case gleam@list:contains(Authorized_clients, Auth_header) of
true ->
Handler(Request);
false ->
forbidden_response()
end
end
end.