use async_trait::async_trait;
#[derive(Debug, Clone, Default)]
pub struct RiskSignal {
pub user_id: String,
pub ip: Option<String>,
pub device_id: Option<String>,
pub known_ip: bool,
pub known_device: bool,
pub country: Option<String>,
pub usual_country: Option<String>,
pub trace_id: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RiskVerdict {
Allow,
StepUp,
Deny,
}
#[async_trait]
pub trait RiskEngine: Send + Sync + 'static {
async fn assess(&self, signal: &RiskSignal) -> RiskVerdict;
}
pub struct HeuristicRiskEngine;
#[async_trait]
impl RiskEngine for HeuristicRiskEngine {
async fn assess(&self, s: &RiskSignal) -> RiskVerdict {
let country_changed = matches!(
(&s.country, &s.usual_country),
(Some(c), Some(u)) if c != u
);
if !s.known_device || country_changed || !s.known_ip {
RiskVerdict::StepUp
} else {
RiskVerdict::Allow
}
}
}