Skip to main content

fission_shell_winit/
biometric.rs

1use fission_core::{
2    BiometricAuthenticateRequest, BiometricAuthenticateResult, BiometricAvailability,
3    BiometricError, BiometricKind, AUTHENTICATE_BIOMETRIC, CANCEL_BIOMETRIC_AUTHENTICATION,
4    GET_BIOMETRIC_AVAILABILITY,
5};
6use fission_shell::async_host::AsyncRegistry;
7use std::sync::Arc;
8
9/// Host-side biometric provider used by shell capability registration.
10pub trait BiometricHost: Send + Sync + 'static {
11    /// Returns local biometric support, enrollment, strength, and modality state.
12    fn availability(&self) -> Result<BiometricAvailability, BiometricError>;
13    /// Prompts the host to verify the current local user.
14    ///
15    /// `request` provides the prompt reason, optional titles, fallback behavior,
16    /// and required strength. The result reports whether verification succeeded.
17    fn authenticate(
18        &self,
19        request: BiometricAuthenticateRequest,
20    ) -> Result<BiometricAuthenticateResult, BiometricError>;
21    /// Cancels an active biometric prompt when the platform permits it.
22    fn cancel_authentication(&self) -> Result<(), BiometricError>;
23}
24
25/// Default provider used when the active shell has no biometric integration.
26#[derive(Debug, Default)]
27pub struct UnsupportedBiometricHost;
28
29impl BiometricHost for UnsupportedBiometricHost {
30    fn availability(&self) -> Result<BiometricAvailability, BiometricError> {
31        Ok(BiometricAvailability {
32            reason: Some("biometric authentication is not supported by this host".into()),
33            ..Default::default()
34        })
35    }
36
37    fn authenticate(
38        &self,
39        _request: BiometricAuthenticateRequest,
40    ) -> Result<BiometricAuthenticateResult, BiometricError> {
41        Err(BiometricError::unsupported("authenticate"))
42    }
43
44    fn cancel_authentication(&self) -> Result<(), BiometricError> {
45        Err(BiometricError::unsupported("cancel_authentication"))
46    }
47}
48
49/// In-process biometric host for tests and non-OS environments.
50#[derive(Debug, Clone)]
51pub struct MemoryBiometricHost {
52    availability: BiometricAvailability,
53    result: BiometricAuthenticateResult,
54}
55
56impl MemoryBiometricHost {
57    pub fn new(availability: BiometricAvailability, result: BiometricAuthenticateResult) -> Self {
58        Self {
59            availability,
60            result,
61        }
62    }
63}
64
65impl Default for MemoryBiometricHost {
66    fn default() -> Self {
67        Self {
68            availability: BiometricAvailability {
69                supported: true,
70                enrolled: true,
71                strong: true,
72                weak: true,
73                device_credential: true,
74                kinds: vec![BiometricKind::Face, BiometricKind::Fingerprint],
75                reason: None,
76            },
77            result: BiometricAuthenticateResult {
78                verified: true,
79                kind: Some(BiometricKind::Face),
80                used_device_credential: false,
81            },
82        }
83    }
84}
85
86impl BiometricHost for MemoryBiometricHost {
87    fn availability(&self) -> Result<BiometricAvailability, BiometricError> {
88        Ok(self.availability.clone())
89    }
90
91    fn authenticate(
92        &self,
93        _request: BiometricAuthenticateRequest,
94    ) -> Result<BiometricAuthenticateResult, BiometricError> {
95        Ok(self.result.clone())
96    }
97
98    fn cancel_authentication(&self) -> Result<(), BiometricError> {
99        Ok(())
100    }
101}
102
103pub(crate) fn register_biometric_capabilities(
104    async_registry: &mut AsyncRegistry,
105    host: Arc<dyn BiometricHost>,
106) {
107    let availability_host = host.clone();
108    async_registry.register_operation_capability(GET_BIOMETRIC_AVAILABILITY, move |(), _| {
109        let host = availability_host.clone();
110        async move { host.availability() }
111    });
112
113    let authenticate_host = host.clone();
114    async_registry.register_operation_capability(AUTHENTICATE_BIOMETRIC, move |request, _| {
115        let host = authenticate_host.clone();
116        async move { host.authenticate(request) }
117    });
118
119    async_registry.register_operation_capability(CANCEL_BIOMETRIC_AUTHENTICATION, move |(), _| {
120        let host = host.clone();
121        async move { host.cancel_authentication() }
122    });
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn unsupported_host_reports_unavailable() {
131        let host = UnsupportedBiometricHost;
132        let availability = host.availability().unwrap();
133
134        assert!(!availability.supported);
135        assert!(host
136            .authenticate(BiometricAuthenticateRequest::default())
137            .is_err());
138    }
139
140    #[test]
141    fn memory_host_authenticates() {
142        let host = MemoryBiometricHost::default();
143        let availability = host.availability().unwrap();
144        assert!(availability.supported);
145
146        let result = host
147            .authenticate(BiometricAuthenticateRequest {
148                reason: "Unlock".into(),
149                ..Default::default()
150            })
151            .unwrap();
152        assert!(result.verified);
153    }
154}