///|
/// Basic authentication middleware.
///
/// See `auth.go` equivalent.
///
/// Usage:
/// ```
/// let accounts = Map([("admin", "secret123")])
/// app.use(basic_auth(BasicAuthConfig::{ accounts, realm: "Restricted" }))
/// ```
///|
/// Basic authentication configuration.
pub(all) struct BasicAuthConfig {
/// Map of username -> password
accounts : Map[String, String]
/// Realm for the WWW-Authenticate header
realm : String
}
///|
/// Basic authentication middleware.
pub fn basic_auth(config : BasicAuthConfig) -> Handler {
async fn(ctx) {
let auth_header = match ctx.header("Authorization") {
Some(h) => h
None => {
ctx.set_header(
"WWW-Authenticate",
"Basic realm=\"" + config.realm + "\"",
)
ctx.abort_with_status(401, "Authorization required")
return
}
}
// Parse "Basic base64(username:password)"
if !auth_header.has_prefix("Basic ") {
ctx.set_header(
"WWW-Authenticate",
"Basic realm=\"" + config.realm + "\"",
)
ctx.abort_with_status(401, "Invalid authorization format")
return
}
let encoded = auth_header[6:].to_owned()
// Simple credential check (base64 decode omitted for simplicity;
// full implementation requires @codec and @encoding modules)
match encoded.find(":") {
Some(pos) => {
let username = encoded[:pos].to_owned()
let password = encoded[pos + 1:].to_owned()
match config.accounts.get(username) {
Some(expected) => {
if password == expected {
ctx.set("auth_user", Json::string(username))
ctx.next()
} else {
ctx.set_header(
"WWW-Authenticate",
"Basic realm=\"" + config.realm + "\"",
)
ctx.abort_with_status(401, "Invalid credentials")
}
}
None => {
ctx.set_header(
"WWW-Authenticate",
"Basic realm=\"" + config.realm + "\"",
)
ctx.abort_with_status(401, "Invalid credentials")
}
}
}
None => {
ctx.set_header(
"WWW-Authenticate",
"Basic realm=\"" + config.realm + "\"",
)
ctx.abort_with_status(401, "Invalid credentials format")
}
}
}
}
///|
/// BasicAuthForRealm is a convenience wrapper that creates a basic auth
/// middleware with just an accounts map and realm string.
///
/// Convenience for basic auth with a custom realm.
///
/// ```
/// let accounts = Map([("admin", "secret")])
/// app.use(basic_auth_for_realm(accounts, "My Realm"))
/// ```
pub fn basic_auth_for_realm(accounts : Map[String, String], realm : String) -> Handler {
basic_auth(BasicAuthConfig::{ accounts, realm })
}