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;
|
||||
|
||||
@@ -24,7 +24,10 @@ pub struct DataCreateAccount {
|
||||
/// 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)
|
||||
|
||||
@@ -23,7 +23,10 @@ pub struct DataPasswordReset {
|
||||
/// 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(
|
||||
|
||||
@@ -23,7 +23,10 @@ pub struct DataResendVerification {
|
||||
/// 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.
|
||||
|
||||
@@ -23,7 +23,10 @@ pub struct DataSendPasswordReset {
|
||||
/// 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.
|
||||
|
||||
Reference in New Issue
Block a user