Packages

Basic HTTP Authentication Scheme for Wisp - just testing gealm export

Current section

Files

Jump to
wisp_basic_auth_courtcircuits src wisp_basic_auth.gleam
Raw

src/wisp_basic_auth.gleam

import gleam/bit_array
import gleam/http/request
import gleam/list
import wisp
// Tuple of client id and password
pub type ClientAuth =
#(String, String)
/// Middleware that validates an `Authorization: Basic` header
/// against a known list of client ids and passwords within a realm.
///
/// The basic authentication scheme is based on the model that the
/// user agent must authenticate itself with a user-ID and a password
/// for each realm.
///
/// The realm value should be considered an opaque string which can
/// only be compared for equality with other realms on that server.
///
/// Example header: `Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==`
///
/// ```gleam
/// use request <- validate_basic_auth("Agrabah", [#("Aladdin", "open sesame")])
/// ```
pub fn validate_basic_auth(
request: wisp.Request,
realm: String,
known_clients: List(ClientAuth),
handler: fn(wisp.Request) -> wisp.Response,
) -> wisp.Response {
case request.get_header(request, "Authorization") {
Error(_) -> unauthorized_response(realm)
Ok(auth_header) -> {
case check_authorization(auth_header, known_clients) {
Ok(_) -> handler(request)
Error(Nil) -> forbidden_response()
}
}
}
}
fn check_authorization(
authorization_header: String,
authorized: List(ClientAuth),
) {
let match = fn(auth) {
let #(id, password) = auth
case encode_creds(id, password) == authorization_header {
True -> Ok(id)
False -> Error(Nil)
}
}
list.find_map(authorized, match)
}
fn encode_creds(id, secret) -> String {
let expected_creds = id <> ":" <> secret
let encoded_creds =
bit_array.base64_encode(bit_array.from_string(expected_creds), True)
"Basic " <> encoded_creds
}
fn unauthorized_response(realm: String) {
let realm = "Basic realm=\"" <> realm <> "\""
401
|> wisp.response()
|> wisp.string_body("Unauthorized")
|> wisp.set_header("WWW-Authenticate", realm)
}
fn forbidden_response() {
403 |> wisp.response() |> wisp.string_body("Forbidden")
}