use std::collections::HashMap;
use security_core::identity::AuthenticatedIdentity;
use security_core::severity::SecuritySeverity;
use security_core::types::ActorId;
use security_events::emit::emit_security_event;
use security_events::event::{EventOutcome, SecurityEvent};
use security_events::kind::EventKind;
use subtle::ConstantTimeEq;
use time::OffsetDateTime;
use crate::authenticator::{private, AuthenticationRequest};
use crate::error::IdentityError;
pub struct ApiKeyAuthenticator {
expected_key: Vec<u8>,
actor_id: ActorId,
roles: Vec<String>,
}
impl ApiKeyAuthenticator {
#[must_use]
pub fn new(expected_key: String, actor_id: ActorId, roles: Vec<String>) -> Self {
Self {
expected_key: expected_key.into_bytes(),
actor_id,
roles,
}
}
}
impl private::Sealed for ApiKeyAuthenticator {}
impl crate::authenticator::Authenticator for ApiKeyAuthenticator {
async fn authenticate(
&self,
request: &AuthenticationRequest,
) -> Result<AuthenticatedIdentity, IdentityError> {
let presented = request.token.as_bytes();
let is_valid = if presented.len() != self.expected_key.len() {
let _ = self.expected_key.ct_eq(&self.expected_key);
false
} else {
presented.ct_eq(&self.expected_key).into()
};
if !is_valid {
emit_security_event(SecurityEvent::new(
EventKind::AuthnFailure,
SecuritySeverity::High,
EventOutcome::Failure,
));
return Err(IdentityError::InvalidCredentials);
}
Ok(AuthenticatedIdentity {
actor_id: self.actor_id.clone(),
tenant_id: None,
roles: self.roles.clone(),
attributes: HashMap::new(),
authenticated_at: OffsetDateTime::now_utc(),
})
}
}