Display an error when attempting to log in to an account that doesn't exist
This commit is contained in:
parent
680114f467
commit
e24cf5c0b8
5 changed files with 67 additions and 17 deletions
|
@ -53,30 +53,32 @@ pub async fn insert_new_user(
|
||||||
.bind(name)
|
.bind(name)
|
||||||
.fetch_one(pool).await
|
.fetch_one(pool).await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!(%err, record = ?new_user, "Cannot insert new user record");
|
error!(?err, record = ?new_user, "Cannot insert new user record");
|
||||||
|
|
||||||
AppError::from(err)
|
AppError::from(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, FromRow)]
|
#[derive(Debug, FromRow)]
|
||||||
pub struct UserIdAndHashedPassword {
|
pub struct UserAndHashedPassword {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
|
pub username: String,
|
||||||
|
pub name: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_username_and_password_by_username(
|
pub async fn get_username_and_password_by_username(
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
username: String,
|
username: String,
|
||||||
) -> Result<UserIdAndHashedPassword, AppError> {
|
) -> Result<UserAndHashedPassword, AppError> {
|
||||||
sqlx::query_as::<_, UserIdAndHashedPassword>(
|
sqlx::query_as::<_, UserAndHashedPassword>(
|
||||||
"SELECT id, password FROM public.user WHERE username = $1;",
|
"SELECT id, username, name, password FROM public.user WHERE username = $1;",
|
||||||
)
|
)
|
||||||
.bind(username)
|
.bind(username)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!(%err, "Unable to find user");
|
error!(?err, "Unable to find user");
|
||||||
AppError::from(err)
|
AppError::from(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -87,7 +89,7 @@ pub async fn verify_user(pool: &DbPool, user_id: i32) -> Result<(), AppError> {
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!(%err, user_id, "Error verifying user");
|
error!(?err, user_id, "Error verifying user");
|
||||||
AppError::from(err)
|
AppError::from(err)
|
||||||
})
|
})
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
|
|
|
@ -81,6 +81,7 @@ impl From<MigrateError> for AppError {
|
||||||
impl From<SqlxError> for AppError {
|
impl From<SqlxError> for AppError {
|
||||||
fn from(other: SqlxError) -> Self {
|
fn from(other: SqlxError) -> Self {
|
||||||
match &other {
|
match &other {
|
||||||
|
SqlxError::RowNotFound => ErrorKind::NoDbRecordFound,
|
||||||
SqlxError::Database(db_err) => {
|
SqlxError::Database(db_err) => {
|
||||||
if let Some(err_code) = db_err.code() {
|
if let Some(err_code) = db_err.code() {
|
||||||
map_db_error_code_to_error_kind(err_code, db_err)
|
map_db_error_code_to_error_kind(err_code, db_err)
|
||||||
|
@ -137,6 +138,7 @@ impl Display for AppError {
|
||||||
f,
|
f,
|
||||||
"Cannot retrieve session: missing required field: {field}"
|
"Cannot retrieve session: missing required field: {field}"
|
||||||
),
|
),
|
||||||
|
ErrorKind::NoDbRecordFound => write!(f, "No database record found"),
|
||||||
ErrorKind::NoSessionFound => write!(f, "No session found"),
|
ErrorKind::NoSessionFound => write!(f, "No session found"),
|
||||||
ErrorKind::Sqlx(err) => write!(f, "{err}"),
|
ErrorKind::Sqlx(err) => write!(f, "{err}"),
|
||||||
ErrorKind::TokenKey => write!(
|
ErrorKind::TokenKey => write!(
|
||||||
|
@ -161,6 +163,7 @@ enum ErrorKind {
|
||||||
InvalidToken,
|
InvalidToken,
|
||||||
MissingEnvironmentVariables(Vec<&'static str>),
|
MissingEnvironmentVariables(Vec<&'static str>),
|
||||||
MissingSessionField(&'static str),
|
MissingSessionField(&'static str),
|
||||||
|
NoDbRecordFound,
|
||||||
NoSessionFound,
|
NoSessionFound,
|
||||||
Sqlx(SqlxError),
|
Sqlx(SqlxError),
|
||||||
TokenKey,
|
TokenKey,
|
||||||
|
@ -181,6 +184,10 @@ impl IntoResponse for AppError {
|
||||||
StatusCode::UNAUTHORIZED,
|
StatusCode::UNAUTHORIZED,
|
||||||
ApiResponse::new_with_error(self).into_json_response(),
|
ApiResponse::new_with_error(self).into_json_response(),
|
||||||
),
|
),
|
||||||
|
&ErrorKind::NoDbRecordFound => (
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
ApiResponse::new_with_error(self).into_json_response(),
|
||||||
|
),
|
||||||
_ => (
|
_ => (
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
ApiResponse::new_with_error(self).into_json_response(),
|
ApiResponse::new_with_error(self).into_json_response(),
|
||||||
|
|
|
@ -4,13 +4,21 @@ use axum::{
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
Json,
|
Json,
|
||||||
};
|
};
|
||||||
|
use http::StatusCode;
|
||||||
|
use pasetors::{keys::SymmetricKey, version4::V4};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{get_username_and_password_by_username, DbPool, UserIdAndHashedPassword},
|
db::{get_username_and_password_by_username, DbPool, UserAndHashedPassword},
|
||||||
models::AppError,
|
models::{ApiResponse, AppError},
|
||||||
requests::AppState,
|
requests::{
|
||||||
services::verify_password,
|
auth::login::models::{AuthLoginResponse, AuthLoginTokenData},
|
||||||
|
AppState,
|
||||||
|
},
|
||||||
|
services::{
|
||||||
|
auth_token::{generate_access_token, generate_auth_token},
|
||||||
|
verify_password,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::models::AuthLoginRequest;
|
use super::models::AuthLoginRequest;
|
||||||
|
@ -21,19 +29,50 @@ pub async fn auth_login_post_handler(
|
||||||
Json(body): Json<AuthLoginRequest>,
|
Json(body): Json<AuthLoginRequest>,
|
||||||
) -> Result<Response, AppError> {
|
) -> Result<Response, AppError> {
|
||||||
let pool = state.db_pool();
|
let pool = state.db_pool();
|
||||||
auth_login_request(pool, body).await
|
let token_key = state.env().token_key();
|
||||||
|
auth_login_request(pool, token_key, body).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn auth_login_request(pool: &DbPool, body: AuthLoginRequest) -> Result<Response, AppError> {
|
async fn auth_login_request(
|
||||||
|
pool: &DbPool,
|
||||||
|
token_key: &SymmetricKey<V4>,
|
||||||
|
body: AuthLoginRequest,
|
||||||
|
) -> Result<Response, AppError> {
|
||||||
debug!(?body);
|
debug!(?body);
|
||||||
|
|
||||||
let AuthLoginRequest { username, password } = body;
|
let AuthLoginRequest { username, password } = body;
|
||||||
let UserIdAndHashedPassword {
|
let UserAndHashedPassword {
|
||||||
id: _id,
|
id: user_id,
|
||||||
|
username,
|
||||||
|
name,
|
||||||
password: hashed_password,
|
password: hashed_password,
|
||||||
} = get_username_and_password_by_username(pool, username).await?;
|
} = get_username_and_password_by_username(pool, username).await?;
|
||||||
|
|
||||||
verify_password(password, hashed_password)?;
|
verify_password(password, hashed_password)?;
|
||||||
|
|
||||||
Ok(().into_response())
|
let (access_token, _access_token_id, access_token_expiration) =
|
||||||
|
generate_access_token(token_key, user_id);
|
||||||
|
|
||||||
|
let (auth_token, _auth_token_id, auth_token_expiration) =
|
||||||
|
generate_auth_token(token_key, user_id);
|
||||||
|
|
||||||
|
let response = AuthLoginResponse {
|
||||||
|
user_id,
|
||||||
|
username,
|
||||||
|
name,
|
||||||
|
access: AuthLoginTokenData {
|
||||||
|
token: access_token,
|
||||||
|
expiration: access_token_expiration,
|
||||||
|
},
|
||||||
|
auth: AuthLoginTokenData {
|
||||||
|
token: auth_token,
|
||||||
|
expiration: auth_token_expiration,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
StatusCode::OK,
|
||||||
|
ApiResponse::new(response).into_json_response(),
|
||||||
|
)
|
||||||
|
.into_response())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,4 +2,4 @@ mod request;
|
||||||
mod response;
|
mod response;
|
||||||
|
|
||||||
pub use request::*;
|
pub use request::*;
|
||||||
// pub use response::*;
|
pub use response::*;
|
||||||
|
|
|
@ -6,6 +6,8 @@ use serde::Serialize;
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AuthLoginResponse {
|
pub struct AuthLoginResponse {
|
||||||
pub user_id: i32,
|
pub user_id: i32,
|
||||||
|
pub username: String,
|
||||||
|
pub name: String,
|
||||||
pub access: AuthLoginTokenData,
|
pub access: AuthLoginTokenData,
|
||||||
pub auth: AuthLoginTokenData,
|
pub auth: AuthLoginTokenData,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue