use anyhow::{Result, anyhow};
use base64::Engine;
use base64::engine::general_purpose::STANDARD as BASE64;
pub fn build_authorization_value(auth: &str) -> Result<String> {
let (scheme, rest) = auth.trim_start().split_once(char::is_whitespace).ok_or_else(|| {
anyhow!(
"auth value '{}' must be in the form '<scheme> <credential>' (e.g. 'bearer {{{{token}}}}')",
auth
)
})?;
let credential = rest.trim();
match scheme.to_lowercase().as_str() {
"bearer" => {
if credential.is_empty() {
return Err(anyhow!("bearer auth is missing a token"));
}
Ok(format!("Bearer {credential}"))
}
"basic" => {
if credential.is_empty() {
return Err(anyhow!(
"basic auth is missing credentials (expected 'user:pass')"
));
}
Ok(format!("Basic {}", BASE64.encode(credential)))
}
other => Err(anyhow!(
"unsupported auth scheme '{}' - supported schemes: bearer, basic",
other
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bearer_happy_path() {
let value = build_authorization_value("bearer abc123").unwrap();
assert_eq!(value, "Bearer abc123");
}
#[test]
fn bearer_is_case_insensitive() {
let value = build_authorization_value("BEARER abc123").unwrap();
assert_eq!(value, "Bearer abc123");
}
#[test]
fn basic_happy_path() {
let value = build_authorization_value("basic user:pass").unwrap();
assert_eq!(value, "Basic dXNlcjpwYXNz");
}
#[test]
fn unknown_scheme_errors() {
let err = build_authorization_value("digest abc123").unwrap_err();
assert!(err.to_string().contains("unsupported auth scheme"));
}
#[test]
fn missing_bearer_token_errors() {
let err = build_authorization_value("bearer ").unwrap_err();
assert!(err.to_string().contains("missing a token"));
}
#[test]
fn missing_basic_credential_errors() {
let err = build_authorization_value("basic ").unwrap_err();
assert!(err.to_string().contains("missing credentials"));
}
#[test]
fn missing_separator_errors() {
let err = build_authorization_value("bearer").unwrap_err();
assert!(err.to_string().contains("must be in the form"));
}
}