-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlogin.rs
More file actions
88 lines (73 loc) · 2.89 KB
/
Copy pathlogin.rs
File metadata and controls
88 lines (73 loc) · 2.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use crate::repositories::user_repository::UserRepository;
use crate::services::auth_service::register::RegisterService;
use argon2::{password_hash::PasswordHash, PasswordVerifier};
use diesel::prelude::*;
pub use payego_primitives::{
config::security_config::SecurityConfig,
error::{ApiError, AuthError},
models::{
app_state::AppState,
dtos::auth_dto::{LoginRequest, LoginResponse},
user::User,
},
};
use tracing::{error, info, warn};
pub struct LoginService;
impl LoginService {
pub async fn login(state: &AppState, payload: LoginRequest) -> Result<LoginResponse, ApiError> {
let mut conn = state.db.get().map_err(|_| {
error!("auth.login: failed to acquire db connection");
ApiError::DatabaseConnection("Database unavailable".into())
})?;
let user = UserRepository::find_by_email(&mut conn, &payload.email)?;
Self::verify_password(&payload.password, user.as_ref())?;
let user = user.ok_or(ApiError::Auth(AuthError::InvalidCredentials))?;
let token = SecurityConfig::create_token(state, &user.id.to_string()).map_err(|_| {
error!("auth.login: jwt creation failed");
ApiError::Internal("Authentication service unavailable".into())
})?;
let refresh_token = Self::create_refresh_token(&mut conn, user.id)?;
info!(
user_id = %user.id,
"User logged in successfully"
);
Ok(LoginResponse {
token,
refresh_token,
user_email: Some(user.email),
})
}
fn verify_password(password: &str, user: Option<&User>) -> Result<(), ApiError> {
// verifying *something* to prevent timing attacks
let hash = user //either get the user password hash or generate a dummy one
.map(|u| u.password_hash.as_str())
.unwrap_or(Self::dummy_hash());
let parsed = PasswordHash::new(hash).map_err(|_| {
error!("auth.login: invalid password hash");
ApiError::Internal("Authentication failure".into())
})?;
let argon2 = RegisterService::create_argon2()?;
if argon2
.verify_password(password.as_bytes(), &parsed)
.is_err()
{
warn!("auth.login: invalid credentials");
return Err(ApiError::Auth(AuthError::InvalidCredentials));
}
Ok(())
}
fn create_refresh_token(
conn: &mut PgConnection,
user_uuid: uuid::Uuid,
) -> Result<String, ApiError> {
super::token::TokenService::generate_refresh_token(conn, user_uuid).map_err(|_| {
error!("auth.login: refresh token creation failed");
ApiError::Internal("Authentication service unavailable".into())
})
}
fn dummy_hash() -> &'static str {
"$argon2id$v=19$m=65536,t=3,p=1$\
c29tZXNhbHQ$\
c29tZWZha2VoYXNo"
}
}