-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/4]). -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", 59). ?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", 86). -spec encode_creds(binary(), binary()) -> binary(). encode_creds(Id, Secret) -> Expected_creds = <<<>/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", 72). -spec check_authorization(binary(), list({binary(), binary()})) -> {ok, binary()} | {error, nil}. check_authorization(Authorization_header, Authorized) -> Match = fun(Auth) -> {Id, Password} = Auth, case encode_creds(Id, Password) =:= Authorization_header of true -> {ok, Id}; false -> {error, nil} end end, gleam@list:find_map(Authorized, Match). -file("src/wisp_basic_auth.gleam", 93). -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", 101). -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", 35). ?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" " use request <- validate_basic_auth(\"Agrabah\", [#(\"Aladdin\", \"open sesame\")])\n" " ```\n" ). -spec validate_basic_auth( gleam@http@request:request(wisp@internal:connection()), binary(), list({binary(), binary()}), fun((gleam@http@request:request(wisp@internal:connection())) -> gleam@http@response:response(wisp:body())) ) -> gleam@http@response:response(wisp:body()). validate_basic_auth(Request, Realm, Known_clients, Handler) -> case gleam@http@request:get_header(Request, <<"Authorization"/utf8>>) of {error, _} -> unauthorized_response(Realm); {ok, Auth_header} -> case check_authorization(Auth_header, Known_clients) of {ok, _} -> Handler(Request); {error, nil} -> forbidden_response() end end.