use super::{AuthError, AuthenticatedPrincipal};
use solo_core::TenantId;
#[derive(Debug, Clone)]
pub struct BearerValidator {
expected_token: String,
default_tenant: TenantId,
}
impl BearerValidator {
pub fn new(expected_token: String, default_tenant: TenantId) -> Self {
Self {
expected_token,
default_tenant,
}
}
pub fn validate(&self, header: Option<&str>) -> Result<AuthenticatedPrincipal, AuthError> {
let header = header.ok_or(AuthError::MissingAuthHeader)?;
let token = header
.strip_prefix("Bearer ")
.ok_or(AuthError::MalformedAuthHeader)?;
if token != self.expected_token {
return Err(AuthError::InvalidBearer);
}
Ok(AuthenticatedPrincipal::bearer(self.default_tenant.clone()))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn validator() -> BearerValidator {
BearerValidator::new("s3cr3t".to_string(), TenantId::default_tenant())
}
#[test]
fn accepts_correct_token() {
let v = validator();
let p = v.validate(Some("Bearer s3cr3t")).expect("accept");
assert_eq!(p.subject, "bearer");
assert_eq!(p.tenant_claim, Some(TenantId::default_tenant()));
assert!(p.scopes.is_empty());
assert!(p.claims.is_null());
}
#[test]
fn rejects_missing_header() {
let v = validator();
let err = v.validate(None).unwrap_err();
assert!(matches!(err, AuthError::MissingAuthHeader), "got {err:?}");
}
#[test]
fn rejects_malformed_header_no_bearer_prefix() {
let v = validator();
let err = v.validate(Some("Basic s3cr3t")).unwrap_err();
assert!(matches!(err, AuthError::MalformedAuthHeader), "got {err:?}");
let err = v.validate(Some("s3cr3t")).unwrap_err();
assert!(matches!(err, AuthError::MalformedAuthHeader), "got {err:?}");
}
#[test]
fn rejects_wrong_token() {
let v = validator();
let err = v.validate(Some("Bearer wrong-token")).unwrap_err();
assert!(matches!(err, AuthError::InvalidBearer), "got {err:?}");
}
}