1use serde::{Deserialize, Serialize};
2use std::cmp::Ordering;
3use std::fmt;
4use utoipa::ToSchema;
5use uuid::Uuid;
6
7use webauthn_rs_proto::PublicKeyCredential;
8use webauthn_rs_proto::RequestChallengeResponse;
9
10#[derive(Debug, Serialize, Deserialize, ToSchema)]
22#[serde(rename_all = "lowercase")]
23pub enum AuthStep {
24 Init(String),
26 Init2 {
30 username: String,
31 issue: AuthIssueSession,
32 #[serde(default)]
33 privileged: bool,
35 },
36 Begin(AuthMech),
38 Cred(AuthCredential),
40}
41
42#[derive(Debug, Serialize, Deserialize, ToSchema)]
44#[serde(rename_all = "lowercase")]
45pub enum AuthState {
46 Choose(Vec<AuthMech>),
48 Continue(Vec<AuthAllowed>),
50 Denied(String),
52 Success(String),
54}
55
56#[derive(Serialize, Deserialize, ToSchema)]
58#[serde(rename_all = "lowercase")]
59pub enum AuthCredential {
60 Anonymous,
61 Password(String),
62 Totp(u32),
63 SecurityKey(Box<PublicKeyCredential>),
64 BackupCode(String),
65 Passkey(Box<PublicKeyCredential>),
67}
68
69impl fmt::Debug for AuthCredential {
70 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
71 match self {
72 AuthCredential::Anonymous => write!(fmt, "Anonymous"),
73 AuthCredential::Password(_) => write!(fmt, "Password(_)"),
74 AuthCredential::Totp(_) => write!(fmt, "TOTP(_)"),
75 AuthCredential::SecurityKey(_) => write!(fmt, "SecurityKey(_)"),
76 AuthCredential::BackupCode(_) => write!(fmt, "BackupCode(_)"),
77 AuthCredential::Passkey(_) => write!(fmt, "Passkey(_)"),
78 }
79 }
80}
81
82#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialOrd, Ord, ToSchema)]
84#[serde(rename_all = "lowercase")]
85pub enum AuthMech {
86 Anonymous,
87 Password,
88 PasswordBackupCode,
89 #[serde(rename = "passwordmfa")]
91 PasswordTotp,
92 PasswordSecurityKey,
93 Passkey,
94}
95
96impl AuthMech {
97 pub fn to_value(&self) -> &'static str {
98 match self {
99 AuthMech::Anonymous => "anonymous",
100 AuthMech::Password => "password",
101 AuthMech::PasswordTotp => "passwordmfa",
102 AuthMech::PasswordBackupCode => "passwordbackupcode",
103 AuthMech::PasswordSecurityKey => "passwordsecuritykey",
104 AuthMech::Passkey => "passkey",
105 }
106 }
107}
108
109impl PartialEq for AuthMech {
110 fn eq(&self, other: &Self) -> bool {
111 std::mem::discriminant(self) == std::mem::discriminant(other)
112 }
113}
114
115impl fmt::Display for AuthMech {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 match self {
118 AuthMech::Anonymous => write!(f, "Anonymous (no credentials)"),
119 AuthMech::Password => write!(f, "Password"),
120 AuthMech::PasswordTotp => write!(f, "TOTP and Password"),
121 AuthMech::PasswordBackupCode => write!(f, "Backup Code and Password"),
122 AuthMech::PasswordSecurityKey => write!(f, "Security Key and Password"),
123 AuthMech::Passkey => write!(f, "Passkey"),
124 }
125 }
126}
127
128#[derive(Debug, Serialize, Deserialize, Copy, Clone, ToSchema)]
130#[serde(rename_all = "lowercase")]
131pub enum AuthIssueSession {
132 Token,
134 Cookie,
136}
137
138#[derive(Debug, Serialize, Deserialize, ToSchema)]
140pub struct AuthRequest {
141 pub step: AuthStep,
142}
143
144#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
148#[serde(rename_all = "lowercase")]
149pub enum AuthAllowed {
150 Anonymous,
151 BackupCode,
152 Password,
153 Totp,
154 SecurityKey(RequestChallengeResponse),
155 Passkey(RequestChallengeResponse),
156}
157
158impl PartialEq for AuthAllowed {
159 fn eq(&self, other: &Self) -> bool {
160 std::mem::discriminant(self) == std::mem::discriminant(other)
161 }
162}
163
164impl From<&AuthAllowed> for u8 {
165 fn from(a: &AuthAllowed) -> u8 {
166 match a {
167 AuthAllowed::Anonymous => 0,
168 AuthAllowed::Password => 1,
169 AuthAllowed::BackupCode => 2,
170 AuthAllowed::Totp => 3,
171 AuthAllowed::Passkey(_) => 4,
172 AuthAllowed::SecurityKey(_) => 5,
173 }
174 }
175}
176
177impl Eq for AuthAllowed {}
178
179impl Ord for AuthAllowed {
180 fn cmp(&self, other: &Self) -> Ordering {
181 let self_ord: u8 = self.into();
182 let other_ord: u8 = other.into();
183 self_ord.cmp(&other_ord)
184 }
185}
186
187impl PartialOrd for AuthAllowed {
188 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
189 Some(self.cmp(other))
190 }
191}
192
193impl fmt::Display for AuthAllowed {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 match self {
196 AuthAllowed::Anonymous => write!(f, "Anonymous (no credentials)"),
197 AuthAllowed::Password => write!(f, "Password"),
198 AuthAllowed::BackupCode => write!(f, "Backup Code"),
199 AuthAllowed::Totp => write!(f, "TOTP"),
200 AuthAllowed::SecurityKey(_) => write!(f, "Security Token"),
201 AuthAllowed::Passkey(_) => write!(f, "Passkey"),
202 }
203 }
204}
205
206#[derive(Debug, Serialize, Deserialize, ToSchema)]
207pub struct AuthResponse {
208 pub sessionid: Uuid,
209 pub state: AuthState,
210}