-
Notifications
You must be signed in to change notification settings - Fork 0
Backend stuff done for the verification #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
623fa62
58a0f1e
6438d6c
abadcea
f0f12a1
274a2ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,9 @@ | ||
| use crate::db::queries::{find_token_by_value, find_user_by_email, find_user_by_username}; | ||
| use crate::features::auth::queries::{delete_user, verify_user}; | ||
| use crate::db::queries::{ | ||
| find_token_by_value, find_user_by_email, find_user_by_id, find_user_by_username, | ||
| }; | ||
| use crate::features::auth::queries::{delete_user, update_verification_token, verify_user}; | ||
| use crate::features::auth::service::new_user; | ||
| use crate::features::auth::types::{AvailabilityQuery, VerifyQuery}; | ||
| use crate::features::auth::types::{AvailabilityQuery, ResendTokenData, VerifyQuery}; | ||
| use crate::state::AppState; | ||
| use crate::utils::api_response::ApiResponse; | ||
| use crate::utils::password::hash_password; | ||
|
|
@@ -142,14 +144,53 @@ pub async fn verify_account( | |
| let expires_at: DateTime<Utc> = row.get("expires_at"); | ||
|
|
||
| if Utc::now() > expires_at { | ||
| return Err(AppError::bad_request("Token has expired")); | ||
| return Err(AppError::gone("Token has expired")); | ||
| } | ||
|
|
||
| verify_user(db, user_id).await?; | ||
|
|
||
| Ok(ApiResponse::ok("Email verified successfully", None)) | ||
| } | ||
|
|
||
| // ---------------------- | ||
| // /api/auth/verify | ||
| // POST | ||
| // Update verification token (for resending verification email) | ||
| // ---------------------- | ||
| pub async fn resend_token( | ||
| State(state): State<AppState>, | ||
| Query(params): Query<ResendTokenData>, | ||
| ) -> Result<ApiResponse<()>, AppError> { | ||
| let token = ¶ms.token; | ||
|
|
||
| let db = state.db()?; | ||
| let result = find_token_by_value(db, token) | ||
| .await? | ||
| .ok_or_else(|| AppError::not_found("Token not found")); | ||
|
|
||
| let user = find_user_by_id(db, result?.get("user_id")) | ||
| .await? | ||
| .ok_or_else(|| AppError::not_found("User not found"))?; | ||
| eprint!("RESULT: {:#?}", user); | ||
|
reijjo marked this conversation as resolved.
Outdated
|
||
|
|
||
| let new_token = update_verification_token(db, user.get("id")).await?; | ||
|
reijjo marked this conversation as resolved.
|
||
|
|
||
| if !state.config.app_env.is_test() | ||
| && let Err(email_err) = state | ||
| .send_verification_email(user.get("email"), &new_token) | ||
| .await | ||
| { | ||
| tracing::error!("Failed to send verification email: {:#?}", email_err); | ||
| return Err(email_err); | ||
| } | ||
|
Comment on lines
+170
to
+188
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Token rotation before email delivery can lock users out after transient mail failures. Line 176 updates the token first; if Line 181–182 email send fails, the previously delivered token is invalidated and the user may no longer have a usable token to retry resend. Suggested direction- let new_token = update_verification_token(db, user.get("id")).await?;
+ // Keep previous token data for compensation if email send fails.
+ let old_token: String = result?.get("token");
+ let old_expires_at: DateTime<Utc> = result?.get("expires_at");
+ let new_token = update_verification_token(db, user.get("id")).await?;
@@
if !state.config.app_env.is_test()
&& let Err(email_err) = state
.email
.send_verification_email(user.get("email"), &new_token)
.await
{
+ // restore previous token state so the last delivered link remains usable
+ restore_verification_token(db, user.get("id"), &old_token, old_expires_at).await?;
tracing::error!("Failed to send verification email: {:#?}", email_err);
return Err(email_err);
}If you want, I can draft a minimal 🤖 Prompt for AI Agents |
||
|
|
||
| Ok(ApiResponse::ok( | ||
| "Verification email resent. Check your inbox.", | ||
| None, | ||
| )) | ||
| } | ||
|
|
||
| // ----------------- | ||
| // Validate data | ||
| // ----------------- | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,7 +4,7 @@ use sqlx::{Executor, Postgres, types::Uuid}; | |||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| use crate::errors::AppError; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Create user - POST | ||||||||||||||||||||||||||||||||||||||||||||||
| // Create user - CREATE | ||||||||||||||||||||||||||||||||||||||||||||||
| pub async fn create_user<'e, E>( | ||||||||||||||||||||||||||||||||||||||||||||||
| db: E, | ||||||||||||||||||||||||||||||||||||||||||||||
| email: &str, | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -27,7 +27,7 @@ where | |||||||||||||||||||||||||||||||||||||||||||||
| Ok(row) | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Create verification token - POST | ||||||||||||||||||||||||||||||||||||||||||||||
| // Create verification token - CREATE | ||||||||||||||||||||||||||||||||||||||||||||||
| pub async fn create_verification_token<'e, E>( | ||||||||||||||||||||||||||||||||||||||||||||||
| db: E, | ||||||||||||||||||||||||||||||||||||||||||||||
| user_id: Uuid, | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -75,3 +75,22 @@ where | |||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Ok(()) | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Update token - UPDATE (for resending verification email) | ||||||||||||||||||||||||||||||||||||||||||||||
| pub async fn update_verification_token<'e, E>(db: E, user_id: Uuid) -> Result<String, AppError> | ||||||||||||||||||||||||||||||||||||||||||||||
| where | ||||||||||||||||||||||||||||||||||||||||||||||
| E: Executor<'e, Database = Postgres>, | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| let new_token = Uuid::new_v4().to_string(); | ||||||||||||||||||||||||||||||||||||||||||||||
| let new_expires_at = Utc::now() + chrono::Duration::hours(24); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| sqlx::query("UPDATE tokens SET token = $1, expires_at = $2 WHERE user_id = $3") | ||||||||||||||||||||||||||||||||||||||||||||||
| .bind(&new_token) | ||||||||||||||||||||||||||||||||||||||||||||||
| .bind(new_expires_at) | ||||||||||||||||||||||||||||||||||||||||||||||
| .bind(user_id) | ||||||||||||||||||||||||||||||||||||||||||||||
| .execute(db) | ||||||||||||||||||||||||||||||||||||||||||||||
| .await | ||||||||||||||||||||||||||||||||||||||||||||||
| .map_err(AppError::Sql)?; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Ok(new_token) | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+87
to
+95
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check Right now, Line 87–95 returns Suggested fix- sqlx::query("UPDATE tokens SET token = $1, expires_at = $2 WHERE user_id = $3")
+ let result = sqlx::query("UPDATE tokens SET token = $1, expires_at = $2 WHERE user_id = $3")
.bind(&new_token)
.bind(new_expires_at)
.bind(user_id)
.execute(db)
.await
.map_err(AppError::Sql)?;
+ if result.rows_affected() != 1 {
+ return Err(AppError::not_found("Verification token not found for user"));
+ }
+
Ok(new_token)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.