use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt;
use utoipa::ToSchema;
use uuid::Uuid;
use webauthn_rs_proto::PublicKeyCredential;
use webauthn_rs_proto::RequestChallengeResponse;
#[derive(Debug, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum AuthStep {
Init(String),
Init2 {
username: String,
issue: AuthIssueSession,
#[serde(default)]
privileged: bool,
},
Begin(AuthMech),
Cred(AuthCredential),
}
#[derive(Debug, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum AuthState {
Choose(Vec<AuthMech>),
Continue(Vec<AuthAllowed>),
Denied(String),
Success(String),
}
#[derive(Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum AuthCredential {
Anonymous,
Password(String),
Totp(u32),
SecurityKey(Box<PublicKeyCredential>),
BackupCode(String),
Passkey(Box<PublicKeyCredential>),
}
impl fmt::Debug for AuthCredential {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
AuthCredential::Anonymous => write!(fmt, "Anonymous"),
AuthCredential::Password(_) => write!(fmt, "Password(_)"),
AuthCredential::Totp(_) => write!(fmt, "TOTP(_)"),
AuthCredential::SecurityKey(_) => write!(fmt, "SecurityKey(_)"),
AuthCredential::BackupCode(_) => write!(fmt, "BackupCode(_)"),
AuthCredential::Passkey(_) => write!(fmt, "Passkey(_)"),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialOrd, Ord, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum AuthMech {
Anonymous,
Password,
PasswordBackupCode,
#[serde(rename = "passwordmfa")]
PasswordTotp,
PasswordSecurityKey,
Passkey,
}
impl AuthMech {
pub fn to_value(&self) -> &'static str {
match self {
AuthMech::Anonymous => "anonymous",
AuthMech::Password => "password",
AuthMech::PasswordTotp => "passwordmfa",
AuthMech::PasswordBackupCode => "passwordbackupcode",
AuthMech::PasswordSecurityKey => "passwordsecuritykey",
AuthMech::Passkey => "passkey",
}
}
}
impl PartialEq for AuthMech {
fn eq(&self, other: &Self) -> bool {
std::mem::discriminant(self) == std::mem::discriminant(other)
}
}
impl fmt::Display for AuthMech {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AuthMech::Anonymous => write!(f, "Anonymous (no credentials)"),
AuthMech::Password => write!(f, "Password"),
AuthMech::PasswordTotp => write!(f, "TOTP and Password"),
AuthMech::PasswordBackupCode => write!(f, "Backup Code and Password"),
AuthMech::PasswordSecurityKey => write!(f, "Security Key and Password"),
AuthMech::Passkey => write!(f, "Passkey"),
}
}
}
#[derive(Debug, Serialize, Deserialize, Copy, Clone, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum AuthIssueSession {
Token,
Cookie,
}
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct AuthRequest {
pub step: AuthStep,
}
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum AuthAllowed {
Anonymous,
BackupCode,
Password,
Totp,
SecurityKey(RequestChallengeResponse),
Passkey(RequestChallengeResponse),
}
impl PartialEq for AuthAllowed {
fn eq(&self, other: &Self) -> bool {
std::mem::discriminant(self) == std::mem::discriminant(other)
}
}
impl From<&AuthAllowed> for u8 {
fn from(a: &AuthAllowed) -> u8 {
match a {
AuthAllowed::Anonymous => 0,
AuthAllowed::Password => 1,
AuthAllowed::BackupCode => 2,
AuthAllowed::Totp => 3,
AuthAllowed::Passkey(_) => 4,
AuthAllowed::SecurityKey(_) => 5,
}
}
}
impl Eq for AuthAllowed {}
impl Ord for AuthAllowed {
fn cmp(&self, other: &Self) -> Ordering {
let self_ord: u8 = self.into();
let other_ord: u8 = other.into();
self_ord.cmp(&other_ord)
}
}
impl PartialOrd for AuthAllowed {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl fmt::Display for AuthAllowed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AuthAllowed::Anonymous => write!(f, "Anonymous (no credentials)"),
AuthAllowed::Password => write!(f, "Password"),
AuthAllowed::BackupCode => write!(f, "Backup Code"),
AuthAllowed::Totp => write!(f, "TOTP"),
AuthAllowed::SecurityKey(_) => write!(f, "Security Token"),
AuthAllowed::Passkey(_) => write!(f, "Passkey"),
}
}
}
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct AuthResponse {
pub sessionid: Uuid,
pub state: AuthState,
}