Skip to main content

robius_authentication/
error.rs

1/// The result of an authentication operation.
2pub type Result<T> = std::result::Result<T, Error>;
3
4/// An error produced during authentication.
5#[derive(Debug, Clone)]
6pub enum Error {
7    // TODO: Reexport jni::errors::Error
8    // TODO: Remove target cfg
9    #[cfg(target_os = "android")]
10    Java(std::sync::Arc<jni::errors::Error>),
11
12    // Common errors
13    /// The user failed to provide valid credentials.
14    Authentication,
15    /// Authentication failed because there were too many failed attempts.
16    #[doc(alias = "lockout")]
17    Exhausted,
18    /// The requested authentication method was unavailable.
19    Unavailable,
20    /// The user canceled authentication.
21    UserCanceled,
22    /// The provided action ID is not in the policy's allowed list.
23    InvalidActionId,
24    /// The prompt text is invalid for this platform — e.g. an empty
25    /// [`Text::apple`] reason or an empty [`AndroidText::title`], which the
26    /// platform APIs reject.
27    ///
28    /// [`Text::apple`]: crate::Text::apple
29    /// [`AndroidText::title`]: crate::AndroidText::title
30    InvalidText,
31
32    // Apple-specific errors
33    /// The app canceled authentication.
34    ///
35    /// This error can occur on:
36    /// - [Apple]
37    ///
38    /// [Apple]: https://developer.apple.com/documentation/localauthentication/laerror/laerrorappcancel
39    AppCanceled,
40    /// The system canceled authentication.
41    ///
42    /// This error can occur on:
43    /// - [Apple]
44    ///
45    /// [Apple]: https://developer.apple.com/documentation/localauthentication/laerror/laerrorsystemcancel
46    SystemCanceled,
47    /// The device supports biometry only using a removable accessory, but the
48    /// paired accessory isn’t connected.
49    ///
50    /// This error can occur on:
51    /// - [Apple]
52    ///
53    /// [Apple]: https://developer.apple.com/documentation/localauthentication/laerror/laerrorbiometrydisconnected
54    BiometryDisconnected,
55    /// The device supports biometry only using a removable accessory, but no
56    /// accessory is paired.
57    ///
58    /// This error can occur on:
59    /// - [Apple]
60    ///
61    /// [Apple]: https://developer.apple.com/documentation/localauthentication/laerror/laerrorbiometrynotpaired
62    NotPaired,
63    /// The user has no enrolled biometric identities.
64    ///
65    /// This error can occur on:
66    /// - [Apple]
67    ///
68    /// [Apple]: https://developer.apple.com/documentation/localauthentication/laerror/laerrorbiometrynotenrolled
69    NotEnrolled,
70    /// Displaying the required authentication user interface is forbidden.
71    ///
72    /// This error can occur on:
73    /// - [Apple]
74    ///
75    /// [Apple]: https://developer.apple.com/documentation/localauthentication/laerror/laerrornotinteractive
76    NotInteractive,
77    /// An attempt to authenticate with an Apple companion device (e.g., Apple Watch) failed.
78    ///
79    /// This error can occur on:
80    /// - [Apple], formerly known as `WatchNotAvailable`
81    ///
82    /// [Apple]: https://developer.apple.com/documentation/localauthentication/laerror-swift.struct/companionnotavailable
83    #[doc(alias = "WatchNotAvailable")]
84    CompanionNotAvailable,
85    /// This error can occur on:
86    /// - [Apple]
87    ///
88    /// [Apple]: https://developer.apple.com/documentation/localauthentication/laerror/laerrorinvaliddimensions
89    InvalidDimensions,
90    /// A passcode isn’t set on the device.
91    ///
92    /// This error can occur on:
93    /// - [Apple]
94    ///
95    /// [Apple]: https://developer.apple.com/documentation/localauthentication/laerror/laerrorpasscodenotset
96    PasscodeNotSet,
97    /// The user tapped the fallback button in the authentication dialog (e.g., "Use Password" instead),
98    /// but you selected an authentication policy that does not support password fallback.
99    ///
100    /// If you get this error, you either must handle the fallback yourself or enable the `password` option
101    /// in the policy builder, which will instruct the system to enable a password fallback option
102    /// in the authentication dialog.
103    ///
104    /// This error can occur on:
105    /// - [Apple]
106    ///
107    /// [Apple]: https://developer.apple.com/documentation/localauthentication/laerror/userfallback
108    UserFallback,
109
110    // Android-specific errors
111    UpdateRequired,
112    Timeout,
113
114    // Windows-specific errors
115    /// The biometric verifier device is performing an operation and is
116    /// unavailable.
117    ///
118    /// This error can occur on:
119    /// - [Windows]
120    ///
121    /// [Windows]: https://learn.microsoft.com/en-us/uwp/api/windows.security.credentials.ui.userconsentverificationresult
122    Busy,
123    /// Group policy has disabled the biometric verifier device.
124    ///
125    /// This error can occur on:
126    /// - [Windows]
127    ///
128    /// [Windows]: https://learn.microsoft.com/en-us/uwp/api/windows.security.credentials.ui.userconsentverificationresult
129    DisabledByPolicy,
130    /// A biometric verifier device is not configured for this user.
131    ///
132    /// This error can occur on:
133    /// - [Windows]
134    ///
135    /// [Windows]: https://learn.microsoft.com/en-us/uwp/api/windows.security.credentials.ui.userconsentverificationresult
136    NotConfigured,
137
138    /// An unknown error occurred.
139    Unknown,
140}
141
142impl std::fmt::Display for Error {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        let description = match self {
145            #[cfg(target_os = "android")]
146            Self::Java(e) => return write!(f, "Java error during authentication: {e}"),
147            Self::Authentication => "the user failed to provide valid credentials",
148            Self::Exhausted => "authentication failed due to too many failed attempts",
149            Self::Unavailable => "the requested authentication method was unavailable",
150            Self::UserCanceled => "the user canceled authentication",
151            Self::InvalidActionId => "the provided action ID is not in the policy's allowed list",
152            Self::InvalidText => "the provided prompt text is invalid for the current platform",
153            Self::AppCanceled => "the app canceled authentication",
154            Self::SystemCanceled => "the system canceled authentication",
155            Self::BiometryDisconnected => {
156                "the paired biometric accessory required for authentication is not connected"
157            }
158            Self::NotPaired => "no biometric accessory required for authentication is paired",
159            Self::NotEnrolled => "the user has no enrolled biometric identities",
160            Self::NotInteractive => "displaying the required authentication UI is forbidden",
161            Self::CompanionNotAvailable => {
162                "authentication with a companion device (e.g., Apple Watch) failed"
163            }
164            Self::InvalidDimensions => "the authentication input has invalid dimensions",
165            Self::PasscodeNotSet => "no passcode is set on the device",
166            Self::UserFallback => {
167                "the user chose the fallback authentication method, \
168                 but the policy does not support fallback"
169            }
170            Self::UpdateRequired => "a security update is required before authenticating",
171            Self::Timeout => "authentication timed out",
172            Self::Busy => "the biometric verifier device is busy",
173            Self::DisabledByPolicy => "group policy has disabled the biometric verifier device",
174            Self::NotConfigured => "no biometric verifier device is configured for this user",
175            Self::Unknown => "an unknown authentication error occurred",
176        };
177        f.write_str(description)
178    }
179}
180
181impl std::error::Error for Error {
182    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
183        #[cfg(target_os = "android")]
184        if let Self::Java(e) = self {
185            return Some(&**e);
186        }
187        None
188    }
189}
190
191#[cfg(target_os = "android")]
192impl From<jni::errors::Error> for Error {
193    fn from(value: jni::errors::Error) -> Self {
194        Self::Java(std::sync::Arc::new(value))
195    }
196}