mirror of
https://github.com/stoatchat/rust-authifier.git
synced 2026-06-30 22:08:36 -04:00
fix: resend verification / reset password if account exists on create
This commit is contained in:
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"rust-analyzer.checkOnSave.command": "clippy"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use okapi::openapi3::{SecuritySchemeData, SecurityScheme};
|
||||
use okapi::openapi3::{SecurityScheme, SecuritySchemeData};
|
||||
use rocket::http::Status;
|
||||
use rocket::outcome::Outcome;
|
||||
use rocket::request::{self, FromRequest};
|
||||
@@ -6,7 +6,7 @@ use rocket::Request;
|
||||
|
||||
use mongodb::bson::DateTime;
|
||||
use rocket_okapi::gen::OpenApiGenerator;
|
||||
use rocket_okapi::request::{RequestHeaderInput, OpenApiFromRequest};
|
||||
use rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
|
||||
use wither::bson::doc;
|
||||
use wither::prelude::*;
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ impl Session {
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "find_one",
|
||||
with: "session"
|
||||
with: "session",
|
||||
})?
|
||||
.ok_or_else(|| Error::InvalidCredentials)
|
||||
}
|
||||
|
||||
+52
-25
@@ -5,7 +5,9 @@ use chrono::{Duration, Utc};
|
||||
use lettre::transport::smtp::authentication::Credentials;
|
||||
use lettre::transport::smtp::client::Tls;
|
||||
use lettre::SmtpTransport;
|
||||
|
||||
use mongodb::bson::DateTime;
|
||||
use mongodb::options::FindOneOptions;
|
||||
use mongodb::Database;
|
||||
use nanoid::nanoid;
|
||||
|
||||
@@ -203,11 +205,6 @@ impl Auth {
|
||||
|
||||
// #region Operations
|
||||
/// Create a new account without validating fields.
|
||||
///
|
||||
/// You should NOT handle errors from this function unless
|
||||
/// if you're debugging this library, it can open you up to
|
||||
/// potential attacks such as email enumeration. Although,
|
||||
/// for something like an admin panel, that's fine.
|
||||
pub async fn create_account(
|
||||
&self,
|
||||
email: String,
|
||||
@@ -223,31 +220,61 @@ impl Auth {
|
||||
// Hash the user's password.
|
||||
let password = self.hash_password(plaintext_password)?;
|
||||
|
||||
// Send email verification.
|
||||
let verification = if verify_email {
|
||||
self.generate_email_verification(email.clone()).await
|
||||
// Check if the account exists first.
|
||||
if let Some(mut account) = Account::find_one(
|
||||
&self.db,
|
||||
doc! {
|
||||
"email_normalised": &email_normalised
|
||||
},
|
||||
FindOneOptions::builder().build(),
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "find_one",
|
||||
with: "account",
|
||||
})? {
|
||||
if let AccountVerification::Pending { .. } = &account.verification {
|
||||
account.verification = self
|
||||
.generate_email_verification(account.email.clone())
|
||||
.await;
|
||||
|
||||
account.save_to_db(&self.db).await?;
|
||||
} else {
|
||||
account.password_reset = self
|
||||
.generate_email_password_reset(account.email.clone())
|
||||
.await;
|
||||
|
||||
account.save_to_db(&self.db).await?;
|
||||
}
|
||||
|
||||
Ok(account)
|
||||
} else {
|
||||
AccountVerification::Verified
|
||||
};
|
||||
// Send email verification.
|
||||
let verification = if verify_email {
|
||||
self.generate_email_verification(email.clone()).await
|
||||
} else {
|
||||
AccountVerification::Verified
|
||||
};
|
||||
|
||||
// Construct new Account.
|
||||
let mut account = Account {
|
||||
id: None,
|
||||
// Construct new Account.
|
||||
let mut account = Account {
|
||||
id: None,
|
||||
|
||||
email,
|
||||
email_normalised,
|
||||
password,
|
||||
email,
|
||||
email_normalised,
|
||||
password,
|
||||
|
||||
disabled: None,
|
||||
verification,
|
||||
password_reset: None,
|
||||
mfa: MultiFactorAuthentication::default(),
|
||||
};
|
||||
disabled: None,
|
||||
verification,
|
||||
password_reset: None,
|
||||
mfa: MultiFactorAuthentication::default(),
|
||||
};
|
||||
|
||||
// Commit to database.
|
||||
account.save_to_db(&self.db).await?;
|
||||
// Commit to database.
|
||||
account.save_to_db(&self.db).await?;
|
||||
|
||||
Ok(account)
|
||||
Ok(account)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new session / login to an account.
|
||||
@@ -306,7 +333,7 @@ impl Auth {
|
||||
doc! {
|
||||
"$set": update
|
||||
},
|
||||
None
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use okapi::openapi3::{SchemaObject, RefOr};
|
||||
use okapi::openapi3::{RefOr, SchemaObject};
|
||||
use regex::Regex;
|
||||
use rocket::http::{ContentType, Status};
|
||||
use rocket::request::Request;
|
||||
|
||||
@@ -17,7 +17,7 @@ pub struct DataChangeEmail {
|
||||
}
|
||||
|
||||
/// # Change Email
|
||||
///
|
||||
///
|
||||
/// Change the associated account email.
|
||||
#[openapi(tag = "Account")]
|
||||
#[patch("/change/email", data = "<data>")]
|
||||
|
||||
@@ -17,7 +17,7 @@ pub struct DataChangePassword {
|
||||
}
|
||||
|
||||
/// # Change Password
|
||||
///
|
||||
///
|
||||
/// Change the current account password.
|
||||
#[openapi(tag = "Account")]
|
||||
#[patch("/change/password", data = "<data>")]
|
||||
|
||||
@@ -20,11 +20,14 @@ pub struct DataCreateAccount {
|
||||
}
|
||||
|
||||
/// # Create Account
|
||||
///
|
||||
///
|
||||
/// Create a new account.
|
||||
#[openapi(tag = "Account")]
|
||||
#[post("/create", data = "<data>")]
|
||||
pub async fn create_account(auth: &State<Auth>, data: Json<DataCreateAccount>) -> Result<EmptyResponse> {
|
||||
pub async fn create_account(
|
||||
auth: &State<Auth>,
|
||||
data: Json<DataCreateAccount>,
|
||||
) -> Result<EmptyResponse> {
|
||||
let data = data.into_inner();
|
||||
|
||||
// Perform validation on given data.
|
||||
@@ -36,16 +39,11 @@ pub async fn create_account(auth: &State<Auth>, data: Json<DataCreateAccount>) -
|
||||
let invite = auth.check_invite(data.invite).await?;
|
||||
|
||||
// Create an account but quietly fail any errors.
|
||||
let account = auth
|
||||
.create_account(data.email, data.password, true)
|
||||
.await
|
||||
.ok();
|
||||
let account = auth.create_account(data.email, data.password, true).await?;
|
||||
|
||||
// Make sure to use up the invite.
|
||||
if let Some(account) = account {
|
||||
if let Some(invite) = invite {
|
||||
invite.claim(&auth.db, account.id.unwrap()).await.ok();
|
||||
}
|
||||
if let Some(invite) = invite {
|
||||
invite.claim(&auth.db, account.id.unwrap()).await.ok();
|
||||
}
|
||||
|
||||
Ok(EmptyResponse)
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::entities::*;
|
||||
use crate::util::Result;
|
||||
|
||||
/// # Fetch Account
|
||||
///
|
||||
///
|
||||
/// Fetch account information from the current session.
|
||||
#[openapi(tag = "Account")]
|
||||
#[get("/")]
|
||||
|
||||
@@ -19,11 +19,14 @@ pub struct DataPasswordReset {
|
||||
}
|
||||
|
||||
/// # Password Reset
|
||||
///
|
||||
///
|
||||
/// Confirm password reset and change the password.
|
||||
#[openapi(tag = "Account")]
|
||||
#[patch("/reset_password", data = "<data>")]
|
||||
pub async fn password_reset(auth: &State<Auth>, data: Json<DataPasswordReset>) -> Result<EmptyResponse> {
|
||||
pub async fn password_reset(
|
||||
auth: &State<Auth>,
|
||||
data: Json<DataPasswordReset>,
|
||||
) -> Result<EmptyResponse> {
|
||||
let data = data.into_inner();
|
||||
|
||||
let mut account = Account::find_one(
|
||||
|
||||
@@ -19,11 +19,14 @@ pub struct DataResendVerification {
|
||||
}
|
||||
|
||||
/// # Resend Verification
|
||||
///
|
||||
///
|
||||
/// Resend account creation verification email.
|
||||
#[openapi(tag = "Account")]
|
||||
#[post("/reverify", data = "<data>")]
|
||||
pub async fn resend_verification(auth: &State<Auth>, data: Json<DataResendVerification>) -> Result<EmptyResponse> {
|
||||
pub async fn resend_verification(
|
||||
auth: &State<Auth>,
|
||||
data: Json<DataResendVerification>,
|
||||
) -> Result<EmptyResponse> {
|
||||
let data = data.into_inner();
|
||||
|
||||
// Perform validation on given data.
|
||||
|
||||
@@ -19,11 +19,14 @@ pub struct DataSendPasswordReset {
|
||||
}
|
||||
|
||||
/// # Send Password Reset
|
||||
///
|
||||
///
|
||||
/// Send an email to reset account password.
|
||||
#[openapi(tag = "Account")]
|
||||
#[post("/reset_password", data = "<data>")]
|
||||
pub async fn send_password_reset(auth: &State<Auth>, data: Json<DataSendPasswordReset>) -> Result<EmptyResponse> {
|
||||
pub async fn send_password_reset(
|
||||
auth: &State<Auth>,
|
||||
data: Json<DataSendPasswordReset>,
|
||||
) -> Result<EmptyResponse> {
|
||||
let data = data.into_inner();
|
||||
|
||||
// Perform validation on given data.
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::logic::Auth;
|
||||
use crate::util::{EmptyResponse, Error, Result};
|
||||
|
||||
/// # Verify Email
|
||||
///
|
||||
///
|
||||
/// Verify an email address.
|
||||
#[openapi(tag = "Account")]
|
||||
#[post("/verify/<code>")]
|
||||
|
||||
@@ -15,7 +15,7 @@ pub struct DataEditSession {
|
||||
}
|
||||
|
||||
/// # Edit Session
|
||||
///
|
||||
///
|
||||
/// Edit current session information.
|
||||
#[openapi(tag = "Session")]
|
||||
#[patch("/<id>", data = "<data>")]
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::logic::Auth;
|
||||
use crate::util::{Error, Result};
|
||||
|
||||
/// # Fetch Sessions
|
||||
///
|
||||
///
|
||||
/// Fetch all sessions associated with this account.
|
||||
#[openapi(tag = "Session")]
|
||||
#[get("/all")]
|
||||
|
||||
@@ -40,7 +40,7 @@ pub enum ResponseLogin {
|
||||
}
|
||||
|
||||
/// # Login
|
||||
///
|
||||
///
|
||||
/// Login to an account.
|
||||
#[openapi(tag = "Session")]
|
||||
#[post("/login", data = "<data>")]
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::logic::Auth;
|
||||
use crate::util::{EmptyResponse, Error, Result};
|
||||
|
||||
/// # Logout
|
||||
///
|
||||
///
|
||||
/// Delete current session.
|
||||
#[openapi(tag = "Session")]
|
||||
#[post("/logout")]
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::logic::Auth;
|
||||
use crate::util::{EmptyResponse, Error, Result};
|
||||
|
||||
/// # Revoke Session
|
||||
///
|
||||
///
|
||||
/// Delete a specific active session.
|
||||
#[openapi(tag = "Session")]
|
||||
#[delete("/<id>")]
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::logic::Auth;
|
||||
use crate::util::{EmptyResponse, Error, Result};
|
||||
|
||||
/// # Delete All Sessions
|
||||
///
|
||||
///
|
||||
/// Delete all active sessions, optionally including current one.
|
||||
#[openapi(tag = "Session")]
|
||||
#[delete("/all?<revoke_self>")]
|
||||
|
||||
Reference in New Issue
Block a user