diff --git a/src/error.rs b/src/error.rs index 35f0d1c4..fb1af239 100644 --- a/src/error.rs +++ b/src/error.rs @@ -19,9 +19,9 @@ impl actix_web::error::ResponseError for AybError { } impl From for AybError { - fn from(cause: fernet::DecryptionError) -> Self { + fn from(_cause: fernet::DecryptionError) -> Self { AybError { - message: format!("{:?}", cause), + message: "Invalid or expired token".to_owned(), } } } diff --git a/src/http/endpoints.rs b/src/http/endpoints.rs index 91b8119f..fa24ef4f 100644 --- a/src/http/endpoints.rs +++ b/src/http/endpoints.rs @@ -8,8 +8,8 @@ use crate::error::AybError; use crate::hosted_db::paths::database_path; use crate::hosted_db::{run_query, QueryResult}; use crate::http::structs::{ - APIKey as APIAPIKey, AuthenticationDetails, AuthenticationMode, AybConfig, - Database as APIDatabase, EmptyResponse, EntityDatabasePath, + APIKey as APIAPIKey, AuthenticationDetails, AybConfig, Database as APIDatabase, EmptyResponse, + EntityDatabasePath, }; use crate::http::tokens::{decrypt_auth_token, encrypt_auth_token}; use crate::http::utils::get_header; @@ -31,58 +31,42 @@ async fn confirm( }) .await?; - // Ensure that there are no verified authentication methods, and - // check to see if this method has been previously attempted but - // not verified. + // Check if there are authentication methods already, and if this + // method in particular is verified. let auth_methods = ayb_db.list_authentication_methods(&created_entity).await?; let mut already_verified = false; - let mut auth_method: Option = None; + let mut found_auth_method: Option = None; for method in auth_methods { - if method.status == (AuthenticationMethodStatus::Verified as i16) { - already_verified = true; - if method.method_type == (AuthenticationMethodType::Email as i16) - && method.email_address == auth_details.email_address - { - auth_method = Some(method) - } + already_verified = true; + if method.method_type == (AuthenticationMethodType::Email as i16) + && method.email_address == auth_details.email_address + { + found_auth_method = Some(method) } } - match AuthenticationMode::from_i16(auth_details.mode) { - AuthenticationMode::Register => { - // If registering, either accept this authentication - // method if it was previously created, or if there is no - // other verification method already verified. - if let None = auth_method { - if already_verified { - return Err(AybError { - message: format!("This entity has already been registered"), - }); - } - ayb_db - .create_authentication_method(&AuthenticationMethod { - entity_id: created_entity.id, - method_type: AuthenticationMethodType::Email as i16, - status: AuthenticationMethodStatus::Verified as i16, - email_address: auth_details.email_address, - }) - .await?; - } - } - AuthenticationMode::Login => { - // TODO(marcua): After creating the login endpoint, - // consider whether this code path is necessary, or if we - // can remove Register vs. Login mode. When doing that, - // think about entity that hasn't registered, has verified - // with the current authentication method, and has - // registered/verified with another authentication method. - if let None = auth_method { - return Err(AybError { - message: format!("Login failed due to unverified authentication method"), - }); - } + if let None = found_auth_method { + // If the user was logging in an already verified account, + // auth_method can't be empty. So the only way to reach this + // branch is when registering. + // When registering, either accept this authentication method + // if it was previously created, or if there is no other + // verification method already verified. + if already_verified { + return Err(AybError { + message: format!("{} has already been registered", created_entity.slug), + }); } + ayb_db + .create_authentication_method(&AuthenticationMethod { + entity_id: created_entity.id, + method_type: AuthenticationMethodType::Email as i16, + status: AuthenticationMethodStatus::Verified as i16, + email_address: auth_details.email_address, + }) + .await?; } + // TODO(marcua): When we implement permissions, get_or_create default API keys. // Ok(HttpResponse::Ok().json(APIAPIKey::from_persisted(&created_key))) Ok(HttpResponse::Ok().json(APIAPIKey { @@ -131,7 +115,6 @@ async fn log_in( let token = encrypt_auth_token( &AuthenticationDetails { version: 1, - mode: AuthenticationMode::Register as i16, entity: entity, entity_type: instantiated_entity.entity_type, email_address: method.email_address.to_owned(), @@ -145,7 +128,7 @@ async fn log_in( } return Err(AybError { - message: format!("No email authentication method for this entity."), + message: format!("No account or email authentication method for {}", entity), }); } @@ -195,14 +178,13 @@ async fn register( if already_verified { return Err(AybError { - message: format!("This entity has already been registered"), + message: format!("{} has already been registered", entity), }); } let token = encrypt_auth_token( &AuthenticationDetails { version: 1, - mode: AuthenticationMode::Register as i16, entity: entity.clone(), entity_type: EntityType::from_str(&entity_type) as i16, email_address: email_address.to_owned(), diff --git a/src/http/structs.rs b/src/http/structs.rs index 26dce0fd..997419d7 100644 --- a/src/http/structs.rs +++ b/src/http/structs.rs @@ -2,10 +2,7 @@ use crate::ayb_db::models::{ DBType, EntityType, InstantiatedDatabase as PersistedDatabase, InstantiatedEntity as PersistedEntity, }; -use clap::ValueEnum; use serde::{Deserialize, Serialize}; -use serde_repr::{Deserialize_repr, Serialize_repr}; -use std::fmt; #[derive(Clone, Serialize, Deserialize)] pub struct AybConfigAuthentication { @@ -72,50 +69,9 @@ pub struct EntityDatabasePath { pub database: String, } -#[derive( - Serialize_repr, Deserialize_repr, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, ValueEnum, -)] -#[repr(i16)] -pub enum AuthenticationMode { - Register = 0, - Login = 1, -} - -impl fmt::Display for AuthenticationMode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl AuthenticationMode { - pub fn from_i16(value: i16) -> AuthenticationMode { - match value { - 0 => AuthenticationMode::Register, - 1 => AuthenticationMode::Login, - _ => panic!("Unknown value: {}", value), - } - } - - pub fn from_str(value: &str) -> AuthenticationMode { - match value { - "register" => AuthenticationMode::Register, - "login" => AuthenticationMode::Login, - _ => panic!("Unknown value: {}", value), - } - } - - pub fn to_str(&self) -> &str { - match self { - AuthenticationMode::Register => "register", - AuthenticationMode::Login => "login", - } - } -} - #[derive(Debug, Serialize, Deserialize)] pub struct AuthenticationDetails { pub version: u16, - pub mode: i16, pub entity: String, pub entity_type: i16, pub email_address: String, diff --git a/src/http/tokens.rs b/src/http/tokens.rs index c0933c20..76d56c04 100644 --- a/src/http/tokens.rs +++ b/src/http/tokens.rs @@ -28,5 +28,8 @@ pub fn decrypt_auth_token( auth_config: &AybConfigAuthentication, ) -> Result { let generator = get_fernet_generator(auth_config)?; - Ok(serde_json::from_slice(&generator.decrypt(&cyphertext)?)?) + Ok(serde_json::from_slice(&generator.decrypt_with_ttl( + &cyphertext, + auth_config.token_expiration_seconds, + )?)?) }