mod client; mod command; mod util; use client::{ email::{EmailMessenger, EmailMessengerError}, store::{Store, StoreError}, }; use email_address::EmailAddress; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; use std::sync::Arc; use strum_macros::{Display, EnumString, EnumVariantNames}; use time::OffsetDateTime; use url::Url; use uuid::Uuid; pub const ENV_AUTH_STORE_CONN_STRING: &str = "SECD_AUTH_STORE_CONN_STRING"; pub const ENV_EMAIL_MESSENGER: &str = "SECD_EMAIL_MESSENGER"; pub const ENV_EMAIL_MESSENGER_CLIENT_ID: &str = "SECD_EMAIL_MESSENGER_CLIENT_ID"; pub const ENV_EMAIL_MESSENGER_CLIENT_SECRET: &str = "SECD_EMAIL_MESSENGER_CLIENT_SECRET"; const SESSION_SIZE_BYTES: usize = 32; const SESSION_DURATION: i64 = 60 /* seconds*/ * 60 /* minutes */ * 24 /* hours */ * 360 /* days */; const EMAIL_VALIDATION_DURATION: i64 = 60 /* seconds*/ * 15 /* minutes */; const ADDRESSS_VALIDATION_CODE_SIZE: u8 = 6; const ADDRESS_VALIDATION_ALLOWS_ATTEMPTS: u8 = 5; const ADDRESS_VALIDATION_IDENTITY_SURJECTION: bool = true; pub type AddressId = Uuid; pub type AddressValidationId = Uuid; pub type CredentialId = Uuid; pub type IdentityId = Uuid; pub type MotifId = Uuid; pub type PhoneNumber = String; pub type RefId = Uuid; pub type SessionToken = String; #[derive(Debug, derive_more::Display, thiserror::Error)] pub enum SecdError { AddressValidationFailed, AddressValidationSessionExchangeFailed, AddressValidationExpiredOrConsumed, TooManyIdentities, IdentityNotFound, EmailMessengerError(#[from] EmailMessengerError), InvalidEmaillAddress(#[from] email_address::Error), FailedToProvideSessionIdentity(String), InvalidSession, StoreError(#[from] StoreError), StoreInitFailure(String), FailedToDecodeInput(#[from] hex::FromHexError), Todo, } pub struct Secd { store: Arc, email_messenger: Arc, } #[derive(Display, Debug, Serialize, Deserialize, EnumString, EnumVariantNames)] #[strum(ascii_case_insensitive)] pub enum AuthStore { Sqlite { conn: String }, Postgres { conn: String }, Redis { conn: String }, } #[derive(Display, Debug, Serialize, Deserialize, EnumString, EnumVariantNames)] #[strum(ascii_case_insensitive)] pub enum AuthEmailMessenger { Local, Ses, Mailgun, Sendgrid, } #[serde_with::skip_serializing_none] #[derive(Debug, Serialize)] pub struct Address { id: AddressId, t: AddressType, #[serde(with = "time::serde::timestamp")] created_at: OffsetDateTime, } #[serde_as] #[serde_with::skip_serializing_none] #[derive(Debug, Serialize)] pub struct AddressValidation { pub id: AddressValidationId, pub identity_id: Option, pub address: Address, pub method: AddressValidationMethod, #[serde(with = "time::serde::timestamp")] pub created_at: OffsetDateTime, #[serde(with = "time::serde::timestamp")] pub expires_at: OffsetDateTime, #[serde(with = "time::serde::timestamp::option")] pub revoked_at: Option, #[serde(with = "time::serde::timestamp::option")] pub validated_at: Option, pub attempts: i32, #[serde_as(as = "serde_with::hex::Hex")] hashed_token: Vec, #[serde_as(as = "serde_with::hex::Hex")] hashed_code: Vec, } #[derive(Debug, Display, Serialize, EnumString)] pub enum AddressValidationMethod { Email, Sms, Oauth, } #[derive(Debug, Display, Serialize, EnumString)] pub enum AddressType { Email { email_address: Option }, Sms { phone_number: Option }, } #[derive(Debug, Serialize)] pub struct Credential { pub id: CredentialId, pub identity_id: IdentityId, pub t: CredentialType, } #[serde_as] #[derive(Debug, Serialize)] pub enum CredentialType { Passphrase { key: String, value: String, }, Oicd { value: String, }, OneTimeCodes { codes: Vec, }, Totp { #[serde_as(as = "DisplayFromStr")] url: Url, code: String, }, WebAuthn { value: String, }, } #[serde_with::skip_serializing_none] #[derive(Debug, Serialize)] pub struct Identity { pub id: IdentityId, pub address_validations: Vec, pub credentials: Vec, pub rules: Vec, // TODO: rules for (e.g. mfa reqs) pub metadata: Option, #[serde(with = "time::serde::timestamp")] pub created_at: OffsetDateTime, #[serde(with = "time::serde::timestamp::option")] pub deleted_at: Option, } #[serde_as] #[serde_with::skip_serializing_none] #[derive(Debug, Serialize)] pub struct Session { pub identity_id: IdentityId, #[serde_as(as = "serde_with::hex::Hex")] #[serde(skip_serializing_if = "Vec::is_empty")] pub token: Vec, #[serde(with = "time::serde::timestamp")] pub created_at: OffsetDateTime, #[serde(with = "time::serde::timestamp")] pub expired_at: OffsetDateTime, #[serde(with = "time::serde::timestamp::option")] pub revoked_at: Option, }