backend/src/api/v1/auth/verify_email.rs
Radical 1ad88725bd
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
feat: use custom middleware for authorization
2025-07-20 14:12:57 +02:00

103 lines
2.3 KiB
Rust

//! `/api/v1/auth/verify-email` Endpoints for verifying user emails
use std::sync::Arc;
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse, Extension,
};
use chrono::{Duration, Utc};
use serde::Deserialize;
use uuid::Uuid;
use crate::{
api::v1::auth::CurrentUser, error::Error, objects::{EmailToken, Me}, AppState
};
#[derive(Deserialize)]
pub struct QueryParams {
token: String,
}
/// `GET /api/v1/auth/verify-email` Verifies user email address
///
/// requires auth? yes
///
/// ### Query Parameters
/// token
///
/// ### Responses
/// 200 Success
///
/// 204 Already verified
///
/// 410 Token Expired
///
/// 404 Not Found
///
/// 401 Unauthorized
///
pub async fn get(
State(app_state): State<Arc<AppState>>,
Query(query): Query<QueryParams>,
Extension(CurrentUser(uuid)): Extension<CurrentUser<Uuid>>
) -> Result<impl IntoResponse, Error> {
let mut conn = app_state.pool.get().await?;
let me = Me::get(&mut conn, uuid).await?;
if me.email_verified {
return Ok(StatusCode::NO_CONTENT);
}
let email_token = EmailToken::get(&app_state, me.uuid).await?;
if query.token != email_token.token {
return Ok(StatusCode::UNAUTHORIZED);
}
me.verify_email(&mut conn).await?;
email_token.delete(&app_state).await?;
Ok(StatusCode::OK)
}
/// `POST /api/v1/auth/verify-email` Sends user verification email
///
/// requires auth? yes
///
/// ### Responses
/// 200 Email sent
///
/// 204 Already verified
///
/// 429 Too Many Requests
///
/// 401 Unauthorized
///
pub async fn post(
State(app_state): State<Arc<AppState>>,
Extension(CurrentUser(uuid)): Extension<CurrentUser<Uuid>>
) -> Result<impl IntoResponse, Error> {
let me = Me::get(&mut app_state.pool.get().await?, uuid).await?;
if me.email_verified {
return Ok(StatusCode::NO_CONTENT);
}
if let Ok(email_token) = EmailToken::get(&app_state, me.uuid).await {
if Utc::now().signed_duration_since(email_token.created_at) > Duration::hours(1) {
email_token.delete(&app_state).await?;
} else {
return Err(Error::TooManyRequests(
"Please allow 1 hour before sending a new email".to_string(),
));
}
}
EmailToken::new(&app_state, me).await?;
Ok(StatusCode::OK)
}