Remove user_id from user creation and verification responses

This commit is contained in:
Z. Charles Dziura 2025-03-06 16:31:10 -05:00
parent 2bdb3ff67a
commit fd04e6445b
7 changed files with 46 additions and 44 deletions

View file

@ -79,6 +79,9 @@ CREATE TABLE IF NOT EXISTS
updated_at TIMESTAMP WITH TIME ZONE NULL updated_at TIMESTAMP WITH TIME ZONE NULL
); );
CREATE INDEX IF NOT EXISTS user_account_permission_user_id_idx ON public.user_account_permission(user_id);
CREATE INDEX IF NOT EXISTS user_account_permission_account_id_idx ON public.user_account_permission(account_id);
CREATE TABLE IF NOT EXISTS CREATE TABLE IF NOT EXISTS
public.budget ( public.budget (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,

View file

@ -1,19 +1,18 @@
use std::{sync::mpsc::Sender, time::SystemTime}; use std::{sync::mpsc::Sender, time::SystemTime};
use crate::{ use crate::{
db::{insert_new_user, DbPool, NewUserEntity, UserEntity}, db::{DbPool, NewUserEntity, UserEntity, insert_new_user},
models::{ApiResponse, AppError, Session}, models::{ApiResponse, AppError, Session},
requests::AppState, requests::AppState,
services::{ services::{
self, auth_token::generate_new_user_token, hash_password, CachePool, self, CachePool, UserConfirmationMessage, auth_token::generate_new_user_token,
UserConfirmationMessage, hash_password,
}, },
}; };
use axum::{ use axum::{
debug_handler, Json, debug_handler,
extract::State, extract::State,
response::{IntoResponse, Response}, response::{IntoResponse, Response},
Json,
}; };
use http::StatusCode; use http::StatusCode;
use pasetors::{keys::SymmetricKey, version4::V4}; use pasetors::{keys::SymmetricKey, version4::V4};
@ -38,6 +37,9 @@ pub async fn user_registration_post_handler(
mail_sender, mail_sender,
) )
.await .await
.map(|(status_code, response)| {
(status_code, ApiResponse::new(response).into_json_response()).into_response()
})
} }
async fn register_new_user_request( async fn register_new_user_request(
@ -47,7 +49,7 @@ async fn register_new_user_request(
signing_key: &SymmetricKey<V4>, signing_key: &SymmetricKey<V4>,
send_verification_email: bool, send_verification_email: bool,
email_sender: &Sender<UserConfirmationMessage>, email_sender: &Sender<UserConfirmationMessage>,
) -> Result<Response, AppError> { ) -> Result<(StatusCode, UserRegistrationResponse), AppError> {
debug!(?body, send_verification_email); debug!(?body, send_verification_email);
let UserRegistrationRequest { let UserRegistrationRequest {
@ -111,22 +113,15 @@ async fn register_new_user_request(
}); });
UserRegistrationResponse { UserRegistrationResponse {
user_id,
expires_at: None, expires_at: None,
session_token: None, session_token: None,
} }
} else { } else {
UserRegistrationResponse { UserRegistrationResponse {
user_id,
expires_at: Some(expires_at), expires_at: Some(expires_at),
session_token: Some(verification_token), session_token: Some(verification_token),
} }
}; };
let response = ( Ok((StatusCode::CREATED, response_body))
StatusCode::CREATED,
ApiResponse::new(response_body).into_json_response(),
);
Ok(response.into_response())
} }

View file

@ -8,8 +8,6 @@ use serde_with::{serde_as, skip_serializing_none};
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UserRegistrationResponse { pub struct UserRegistrationResponse {
pub user_id: i32,
#[serde(serialize_with = "humantime_serde::serialize")] #[serde(serialize_with = "humantime_serde::serialize")]
pub expires_at: Option<SystemTime>, pub expires_at: Option<SystemTime>,

View file

@ -2,8 +2,8 @@ mod create;
mod verify; mod verify;
use axum::{ use axum::{
routing::{get, post},
Router, Router,
routing::{get, post},
}; };
use create::user_registration_post_handler; use create::user_registration_post_handler;
@ -17,7 +17,7 @@ pub fn requests(state: AppState) -> Router {
"/user", "/user",
Router::new() Router::new()
.route("/", post(user_registration_post_handler)) .route("/", post(user_registration_post_handler))
.route("/{user_id}/verify", get(user_verification_get_handler)), .route("/verify", get(user_verification_get_handler)),
) )
.with_state(state.clone()) .with_state(state.clone())
} }

View file

@ -2,23 +2,22 @@ use std::str::FromStr;
use axum::{ use axum::{
debug_handler, debug_handler,
extract::{Path, Query, State}, extract::{Query, State},
response::{IntoResponse, Response}, response::{IntoResponse, Response},
Json,
}; };
use http::StatusCode; use http::StatusCode;
use pasetors::{claims::ClaimsValidationRules, keys::SymmetricKey, version4::V4}; use pasetors::{claims::ClaimsValidationRules, keys::SymmetricKey, version4::V4};
use tracing::{debug, error}; use tracing::error;
use uuid::Uuid; use uuid::Uuid;
use crate::{ use crate::{
db::{verify_user, DbPool}, db::{DbPool, verify_user},
models::{ApiResponse, AppError}, models::{ApiResponse, AppError},
requests::{ requests::{
auth::generate_login_auth_and_session_tokens, AppState, auth::generate_login_auth_and_session_tokens,
user::verify::UserVerifyGetResponseTokenAndExpiration, AppState, user::verify::UserVerifyGetResponseTokenAndExpiration,
}, },
services::{auth_token::verify_token, user_session, CachePool}, services::{CachePool, auth_token::verify_token, user_session},
}; };
use super::{UserVerifyGetParams, UserVerifyGetResponse}; use super::{UserVerifyGetParams, UserVerifyGetResponse};
@ -26,7 +25,6 @@ use super::{UserVerifyGetParams, UserVerifyGetResponse};
#[debug_handler] #[debug_handler]
pub async fn user_verification_get_handler( pub async fn user_verification_get_handler(
State(state): State<AppState>, State(state): State<AppState>,
Path(user_id): Path<i32>,
Query(query): Query<UserVerifyGetParams>, Query(query): Query<UserVerifyGetParams>,
) -> Result<Response, AppError> { ) -> Result<Response, AppError> {
let db_pool = state.db_pool(); let db_pool = state.db_pool();
@ -34,21 +32,22 @@ pub async fn user_verification_get_handler(
let token_key = state.config().secrets().token_key(); let token_key = state.config().secrets().token_key();
let UserVerifyGetParams { verification_token } = query; let UserVerifyGetParams { verification_token } = query;
verify_new_user_request(db_pool, cache_pool, user_id, verification_token, token_key).await verify_new_user_request(db_pool, cache_pool, verification_token, token_key)
.await
.map(|(status_code, response)| {
(status_code, ApiResponse::new(response).into_json_response()).into_response()
})
} }
async fn verify_new_user_request( async fn verify_new_user_request(
db_pool: &DbPool, db_pool: &DbPool,
cache_pool: &CachePool, cache_pool: &CachePool,
user_id: i32,
verification_token: String, verification_token: String,
token_key: &SymmetricKey<V4>, token_key: &SymmetricKey<V4>,
) -> Result<Response, AppError> { ) -> Result<(StatusCode, UserVerifyGetResponse), AppError> {
debug!(user_id);
let validation_rules = { let validation_rules = {
let mut rules = ClaimsValidationRules::new(); let mut rules = ClaimsValidationRules::new();
rules.validate_audience_with(format!("/user/{user_id}/verify").as_str()); rules.validate_audience_with(format!("/user/verify").as_str());
rules rules
}; };
@ -59,11 +58,24 @@ async fn verify_new_user_request(
) )
.inspect_err(|err| error!(?err))?; .inspect_err(|err| error!(?err))?;
let verification_token_id = verified_token let (user_id, verification_token_id) = verified_token
.payload_claims() .payload_claims()
.map(|claims| claims.get_claim("jti")) .map(|claims| {
(
claims
.get_claim("sub")
.map(|sub| sub.as_str())
.flatten() .flatten()
.map(|jti| Uuid::from_str(jti.as_str().unwrap()).unwrap()) .map(|sub| sub.parse::<i32>().unwrap())
.unwrap(),
claims
.get_claim("jti")
.map(|jti| jti.as_str())
.flatten()
.unwrap(),
)
})
.map(|(user_id, jti)| (user_id, Uuid::from_str(jti).unwrap()))
.unwrap(); .unwrap();
user_session::exists(cache_pool, verification_token_id) user_session::exists(cache_pool, verification_token_id)
@ -88,7 +100,6 @@ async fn verify_new_user_request(
generate_login_auth_and_session_tokens(cache_pool, token_key, user_id).await?; generate_login_auth_and_session_tokens(cache_pool, token_key, user_id).await?;
let response = UserVerifyGetResponse { let response = UserVerifyGetResponse {
user_id,
session: UserVerifyGetResponseTokenAndExpiration { session: UserVerifyGetResponseTokenAndExpiration {
token: session_token, token: session_token,
expires_at: session_token_expiration, expires_at: session_token_expiration,
@ -99,9 +110,5 @@ async fn verify_new_user_request(
}, },
}; };
Ok(( Ok((StatusCode::OK, response))
StatusCode::OK,
Json(ApiResponse::<UserVerifyGetResponse>::new(response)),
)
.into_response())
} }

View file

@ -5,7 +5,6 @@ use serde::Serialize;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UserVerifyGetResponse { pub struct UserVerifyGetResponse {
pub user_id: i32,
pub session: UserVerifyGetResponseTokenAndExpiration, pub session: UserVerifyGetResponseTokenAndExpiration,
pub auth: UserVerifyGetResponseTokenAndExpiration, pub auth: UserVerifyGetResponseTokenAndExpiration,
} }

View file

@ -96,7 +96,7 @@ pub fn generate_new_user_token(key: &SymmetricKey<V4>, user_id: i32) -> (String,
key, key,
user_id, user_id,
FIFTEEN_MINUTES, FIFTEEN_MINUTES,
Some(format!("/user/{user_id}/verify").as_str()), Some(format!("/user/verify").as_str()),
) )
} }