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
64 #[schema(value_type = HashMap<String, Value>)]
65 SecurityKey(Box<PublicKeyCredential>),
66 BackupCode(String),
67 #[schema(value_type = String)]
69 Passkey(Box<PublicKeyCredential>),
70}
71
72impl fmt::Debug for AuthCredential {
73 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
74 match self {
75 AuthCredential::Anonymous => write!(fmt, "Anonymous"),
76 AuthCredential::Password(_) => write!(fmt, "Password(_)"),
77 AuthCredential::Totp(_) => write!(fmt, "TOTP(_)"),
78 AuthCredential::SecurityKey(_) => write!(fmt, "SecurityKey(_)"),
79 AuthCredential::BackupCode(_) => write!(fmt, "BackupCode(_)"),
80 AuthCredential::Passkey(_) => write!(fmt, "Passkey(_)"),
81 }
82 }
83}
84
85#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialOrd, Ord, ToSchema)]
87#[serde(rename_all = "lowercase")]
88pub enum AuthMech {
89 Anonymous,
90 Password,
91 PasswordBackupCode,
92 #[serde(rename = "passwordmfa")]
94 PasswordTotp,
95 PasswordSecurityKey,
96 Passkey,
97 OAuth2Trust,
98}
99
100impl AuthMech {
101 pub fn to_value(&self) -> &'static str {
102 match self {
103 AuthMech::Anonymous => "anonymous",
104 AuthMech::Password => "password",
105 AuthMech::PasswordTotp => "passwordmfa",
106 AuthMech::PasswordBackupCode => "passwordbackupcode",
107 AuthMech::PasswordSecurityKey => "passwordsecuritykey",
108 AuthMech::Passkey => "passkey",
109 AuthMech::OAuth2Trust => "oauth2trust",
110 }
111 }
112}
113
114impl PartialEq for AuthMech {
115 fn eq(&self, other: &Self) -> bool {
116 std::mem::discriminant(self) == std::mem::discriminant(other)
117 }
118}
119
120impl fmt::Display for AuthMech {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match self {
123 AuthMech::Anonymous => write!(f, "Anonymous (no credentials)"),
124 AuthMech::Password => write!(f, "Password"),
125 AuthMech::PasswordTotp => write!(f, "TOTP and Password"),
126 AuthMech::PasswordBackupCode => write!(f, "Backup Code and Password"),
127 AuthMech::PasswordSecurityKey => write!(f, "Security Key and Password"),
128 AuthMech::Passkey => write!(f, "Passkey"),
129 AuthMech::OAuth2Trust => write!(f, "OAuth2 Trust"),
130 }
131 }
132}
133
134#[derive(Debug, Serialize, Deserialize, Copy, Clone, ToSchema)]
136#[serde(rename_all = "lowercase")]
137pub enum AuthIssueSession {
138 Token,
140 Cookie,
142}
143
144impl std::fmt::Display for AuthIssueSession {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 match self {
147 AuthIssueSession::Token => write!(f, "Token"),
148 AuthIssueSession::Cookie => write!(f, "Cookie"),
149 }
150 }
151}
152
153#[derive(Debug, Serialize, Deserialize, ToSchema)]
155pub struct AuthRequest {
156 pub step: AuthStep,
157}
158
159#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
163#[serde(rename_all = "lowercase")]
164pub enum AuthAllowed {
165 Anonymous,
166 BackupCode,
167 Password,
168 Totp,
169
170 #[schema(value_type = HashMap<String, Value>)]
171 SecurityKey(RequestChallengeResponse),
172 #[schema(value_type = HashMap<String, Value>)]
173 Passkey(RequestChallengeResponse),
174}
175
176impl PartialEq for AuthAllowed {
177 fn eq(&self, other: &Self) -> bool {
178 std::mem::discriminant(self) == std::mem::discriminant(other)
179 }
180}
181
182impl From<&AuthAllowed> for u8 {
183 fn from(a: &AuthAllowed) -> u8 {
184 match a {
185 AuthAllowed::Anonymous => 0,
186 AuthAllowed::Password => 1,
187 AuthAllowed::BackupCode => 2,
188 AuthAllowed::Totp => 3,
189 AuthAllowed::Passkey(_) => 4,
190 AuthAllowed::SecurityKey(_) => 5,
191 }
192 }
193}
194
195impl Eq for AuthAllowed {}
196
197impl Ord for AuthAllowed {
198 fn cmp(&self, other: &Self) -> Ordering {
199 let self_ord: u8 = self.into();
200 let other_ord: u8 = other.into();
201 self_ord.cmp(&other_ord)
202 }
203}
204
205impl PartialOrd for AuthAllowed {
206 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
207 Some(self.cmp(other))
208 }
209}
210
211impl fmt::Display for AuthAllowed {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 match self {
214 AuthAllowed::Anonymous => write!(f, "Anonymous (no credentials)"),
215 AuthAllowed::Password => write!(f, "Password"),
216 AuthAllowed::BackupCode => write!(f, "Backup Code"),
217 AuthAllowed::Totp => write!(f, "TOTP"),
218 AuthAllowed::SecurityKey(_) => write!(f, "Security Token"),
219 AuthAllowed::Passkey(_) => write!(f, "Passkey"),
220 }
221 }
222}
223
224#[derive(Debug, Serialize, Deserialize, ToSchema)]
225pub struct AuthResponse {
226 pub sessionid: Uuid,
227 pub state: AuthState,
228}