Face Auth
A Linux PAM module for facial recognition login using IR cameras and Windows Hello–compatible hardware. Features liveness anti-spoofing, an egui graphical configuration tool, and plugs into any PAM stack as a drop-in module.




face-auth is a Linux PAM module written in Rust that authenticates logins using facial recognition, targeting IR cameras and Windows Hello–compatible infrared hardware so it isn't fooled by a printed photo or a phone screen the way plain RGB face-unlock can be. A liveness/anti-spoofing check runs before a match is accepted, and a separate egui-based graphical configuration tool handles enrolling faces and tuning thresholds without hand-editing config files. Because it's a standard PAM module, it drops into any PAM-aware stack — login, sudo, a display manager — alongside or instead of password auth, with the actual recognition and liveness logic implemented in Rust for memory safety in a process that runs with elevated authentication privileges.
Architecture
graph TD IR["IR camera frame"] --> SCRFD["SCRFD face detector"] SCRFD -->|BBox + 5-point landmarks| Geometry["analyze_geometry()
yaw/pitch/roll, distance ratio"] Geometry --> Metrics["FaceMetrics"] Metrics --> Classify["classify()
thresholds from GeometryConfig"] Classify -->|liveness/framing ok| StateMachine["StateMachine
Guidance -> Authenticating"] StateMachine -->|match| Embeddings[("enrolled embeddings")] Embeddings --> PAM["PAM module
accept / reject"]
Under the hood
Geometry checks turn raw 5-point landmarks into yaw/pitch/roll and a distance ratio, then a priority-ordered classifier decides what feedback to show — IR saturation and occluded eyes take precedence over framing issues.
fn classify(m: &FaceMetrics, cfg: &GeometryConfig) -> FeedbackState {
if m.ir_saturated {
return FeedbackState::IRSaturated;
}
if !m.eyes_visible {
return FeedbackState::EyesNotVisible;
}
if m.face_width_ratio < cfg.distance_min {
return FeedbackState::TooFar;
}
if m.face_width_ratio > cfg.distance_max {
return FeedbackState::TooClose;
}
if m.roll_deg.abs() > cfg.roll_max_deg {
return FeedbackState::LookAtCamera;
}
if m.yaw_deg > cfg.yaw_max_deg {
return FeedbackState::TurnLeft; // turned right → tell user to turn left
}
if m.yaw_deg < -cfg.yaw_max_deg {
return FeedbackState::TurnRight; // turned left → tell user to turn right
}
if m.pitch_deg > cfg.pitch_max_deg {
return FeedbackState::TiltUp;
}
if m.pitch_deg < -cfg.pitch_max_deg {
return FeedbackState::TiltDown;
}
FeedbackState::Authenticating
}