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
144#[derive(Debug, Serialize, Deserialize, ToSchema)]
146pub struct AuthRequest {
147 pub step: AuthStep,
148}
149
150#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
154#[serde(rename_all = "lowercase")]
155pub enum AuthAllowed {
156 Anonymous,
157 BackupCode,
158 Password,
159 Totp,
160
161 #[schema(value_type = HashMap<String, Value>)]
162 SecurityKey(RequestChallengeResponse),
163 #[schema(value_type = HashMap<String, Value>)]
164 Passkey(RequestChallengeResponse),
165}
166
167impl PartialEq for AuthAllowed {
168 fn eq(&self, other: &Self) -> bool {
169 std::mem::discriminant(self) == std::mem::discriminant(other)
170 }
171}
172
173impl From<&AuthAllowed> for u8 {
174 fn from(a: &AuthAllowed) -> u8 {
175 match a {
176 AuthAllowed::Anonymous => 0,
177 AuthAllowed::Password => 1,
178 AuthAllowed::BackupCode => 2,
179 AuthAllowed::Totp => 3,
180 AuthAllowed::Passkey(_) => 4,
181 AuthAllowed::SecurityKey(_) => 5,
182 }
183 }
184}
185
186impl Eq for AuthAllowed {}
187
188impl Ord for AuthAllowed {
189 fn cmp(&self, other: &Self) -> Ordering {
190 let self_ord: u8 = self.into();
191 let other_ord: u8 = other.into();
192 self_ord.cmp(&other_ord)
193 }
194}
195
196impl PartialOrd for AuthAllowed {
197 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
198 Some(self.cmp(other))
199 }
200}
201
202impl fmt::Display for AuthAllowed {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 match self {
205 AuthAllowed::Anonymous => write!(f, "Anonymous (no credentials)"),
206 AuthAllowed::Password => write!(f, "Password"),
207 AuthAllowed::BackupCode => write!(f, "Backup Code"),
208 AuthAllowed::Totp => write!(f, "TOTP"),
209 AuthAllowed::SecurityKey(_) => write!(f, "Security Token"),
210 AuthAllowed::Passkey(_) => write!(f, "Passkey"),
211 }
212 }
213}
214
215#[derive(Debug, Serialize, Deserialize, ToSchema)]
216pub struct AuthResponse {
217 pub sessionid: Uuid,
218 pub state: AuthState,
219}