register refresh token and login refresh token for multiple users
This commit is contained in:
BIN
db/1.sqlite-shm
Normal file
BIN
db/1.sqlite-shm
Normal file
Binary file not shown.
0
db/1.sqlite-wal
Normal file
0
db/1.sqlite-wal
Normal file
0
db/2.sqlite
Normal file
0
db/2.sqlite
Normal file
Binary file not shown.
Binary file not shown.
@@ -60,7 +60,7 @@ std::panic::set_hook(Box::new(|info| {
|
|||||||
.send();
|
.send();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
panic!("crash-test");
|
//panic!("crash-test");
|
||||||
|
|
||||||
let hotel_pools = HotelPool::new();
|
let hotel_pools = HotelPool::new();
|
||||||
let logs_manager = SqliteConnectionManager::file("db/auth.sqlite");
|
let logs_manager = SqliteConnectionManager::file("db/auth.sqlite");
|
||||||
|
|||||||
@@ -329,6 +329,11 @@ struct LoginResponse {
|
|||||||
token: String,
|
token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct MultiLoginResponse {
|
||||||
|
tokens: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn clean_auth_loging(
|
pub async fn clean_auth_loging(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Extension(keys): Extension<JwtKeys>,
|
Extension(keys): Extension<JwtKeys>,
|
||||||
@@ -428,31 +433,55 @@ pub async fn create_refresh_token(
|
|||||||
let conn = state.logs_pool.get()
|
let conn = state.logs_pool.get()
|
||||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error".to_string()))?;
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error".to_string()))?;
|
||||||
|
|
||||||
let user_row = conn.query_row(
|
let mut stmt = conn.prepare(
|
||||||
"SELECT id, password, hotel_id FROM users WHERE username = ?1",
|
"SELECT id, password, hotel_id FROM users WHERE username = ?1"
|
||||||
params![&payload.username],
|
).map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB prepare error".to_string()))?;
|
||||||
|row| {
|
|
||||||
|
|
||||||
|
let user_rows = stmt
|
||||||
|
.query_map(params![&payload.username], |row| {
|
||||||
let user_id: i32 = row.get(0)?;
|
let user_id: i32 = row.get(0)?;
|
||||||
let password: String = row.get(1)?;
|
let password: String = row.get(1)?;
|
||||||
let hotel_id: i32 = row.get(2)?;
|
let hotel_id: i32 = row.get(2)?;
|
||||||
Ok((user_id, password, hotel_id))
|
Ok((user_id, password, hotel_id))
|
||||||
},
|
})
|
||||||
).optional()
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB query error".to_string()))?;
|
||||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB get user id error".to_string()))?;
|
|
||||||
|
|
||||||
|
/*
|
||||||
let (user_id, stored_hash, hotel_id) = user_row
|
let (user_id, stored_hash, hotel_id) = user_row
|
||||||
.ok_or((StatusCode::NOT_FOUND, "User not found".to_string()))?;
|
.ok_or((StatusCode::NOT_FOUND, "User not found".to_string()))?;
|
||||||
|
*/
|
||||||
|
//let mut tokens = Vec::new();
|
||||||
|
|
||||||
|
for user_row_result in user_rows {
|
||||||
|
let (user_id, stored_hash, hotel_id) = user_row_result
|
||||||
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB row error".to_string()))?;
|
||||||
|
|
||||||
if !verify_password(&payload.password, &stored_hash) {
|
if !verify_password(&payload.password, &stored_hash) {
|
||||||
return Err((StatusCode::UNAUTHORIZED, "Invalid credentials".to_string()));
|
continue; // Skip rows with invalid password
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
let mut bytes = [0u8; 64];
|
||||||
|
OsRng.fill_bytes(&mut bytes);
|
||||||
|
let raw_token = Uuid::new_v4().to_string();
|
||||||
|
|
||||||
|
let hashed_token = argon2
|
||||||
|
.hash_password(raw_token.as_bytes(), &salt)
|
||||||
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
|
||||||
|
.to_string();
|
||||||
|
*/
|
||||||
|
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO refresh_token (user_id, token_hash, device_id, user_agent, hotel_id) VALUES (?1, ?2, ?3, ?4, ?5)",
|
"INSERT INTO refresh_token (user_id, token_hash, device_id, user_agent, hotel_id) VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||||
params![user_id, hashed_token, device_id_str, user_agent_str, hotel_id],
|
params![user_id, hashed_token, device_id_str, user_agent_str, hotel_id],
|
||||||
)
|
)
|
||||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB insert error".to_string()))?;
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB insert error".to_string()))?;
|
||||||
|
|
||||||
|
//tokens.push(raw_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let cookie_value = format!("refresh_token={}; HttpOnly; Secure; Path=/", raw_token);
|
let cookie_value = format!("refresh_token={}; HttpOnly; Secure; Path=/", raw_token);
|
||||||
|
|
||||||
let mut response = (StatusCode::CREATED, "Refresh token created successfully").into_response();
|
let mut response = (StatusCode::CREATED, "Refresh token created successfully").into_response();
|
||||||
@@ -482,36 +511,62 @@ pub async fn login_refresh_token (
|
|||||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error").into_response(),
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error").into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_agent_str = user_agent
|
|
||||||
|
/*let user_agent_str = user_agent
|
||||||
.map(|ua| ua.to_string())
|
.map(|ua| ua.to_string())
|
||||||
.unwrap_or_else(|| "Unknown".to_string());
|
.unwrap_or_else(|| "Unknown".to_string());
|
||||||
|
|
||||||
let device_id_str = payload.device_id.to_string();
|
let device_id_str = payload.device_id.to_string();
|
||||||
|
*/
|
||||||
|
|
||||||
|
let user_agent_str = match user_agent {
|
||||||
|
Some(ua) => ua.to_string(),
|
||||||
|
None => return (StatusCode::INTERNAL_SERVER_ERROR, "user agent unknown").into_response(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let device_id_str = payload.device_id.to_string();
|
||||||
|
|
||||||
|
|
||||||
//"SELECT user_id, token_hash, hotel_id FROM refresh_token WHERE device_id = ?1 AND user_agent = ?2",
|
//"SELECT user_id, token_hash, hotel_id FROM refresh_token WHERE device_id = ?1 AND user_agent = ?2",
|
||||||
|
|
||||||
let device_row = match conn.query_row(
|
let mut stmt = match conn.prepare(
|
||||||
"SELECT user_id, token_hash, hotel_id FROM refresh_token WHERE device_id = ?1 AND user_agent = ?2",
|
"SELECT user_id, token_hash, hotel_id
|
||||||
params![&device_id_str, &user_agent_str],
|
FROM refresh_token
|
||||||
|row| {
|
WHERE device_id = ?1 AND user_agent = ?2"
|
||||||
let user_id: i32 = row.get(0)?;
|
) {
|
||||||
let token_hash: String = row.get(1)?;
|
Ok(s) => s,
|
||||||
let hotel_id: i32 = row.get(2)?;
|
|
||||||
//let displayname: String = row.get(3)?;
|
|
||||||
Ok((user_id, token_hash, hotel_id))
|
|
||||||
},
|
|
||||||
).optional() {
|
|
||||||
Ok(opt) => opt,
|
|
||||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error").into_response(),
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error").into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (user_id, token_hash, hotel_id) = match device_row {
|
let rows = match stmt.query_map(params![&device_id_str, &user_agent_str], |row| {
|
||||||
Some(tuple) => tuple,
|
Ok((
|
||||||
None => return (StatusCode::UNAUTHORIZED, "No matching device").into_response(),
|
row.get::<_, i32>(0)?, // user_id
|
||||||
|
row.get::<_, String>(1)?, // token_hash
|
||||||
|
row.get::<_, i32>(2)?, // hotel_id
|
||||||
|
))
|
||||||
|
}) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error").into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut entries = Vec::new();
|
||||||
|
for r in rows {
|
||||||
|
match r {
|
||||||
|
Ok(t) => entries.push(t),
|
||||||
|
Err(_) => continue, // ignore corrupt rows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if entries.is_empty() {
|
||||||
|
return (StatusCode::UNAUTHORIZED, "No matching device").into_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tokens = Vec::new();
|
||||||
|
|
||||||
|
for (user_id, token_hash, hotel_id) in entries {
|
||||||
if !verify_password(&payload.refresh_token, &token_hash) {
|
if !verify_password(&payload.refresh_token, &token_hash) {
|
||||||
return (StatusCode::UNAUTHORIZED, "Invalid or mismatched token").into_response();
|
// skip rows with wrong hash
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let expiration = match chrono::Utc::now().checked_add_signed(chrono::Duration::hours(15)) {
|
let expiration = match chrono::Utc::now().checked_add_signed(chrono::Duration::hours(15)) {
|
||||||
@@ -525,19 +580,24 @@ pub async fn login_refresh_token (
|
|||||||
let claims = serde_json::json!({
|
let claims = serde_json::json!({
|
||||||
"id": user_id,
|
"id": user_id,
|
||||||
"hotel_id": hotel_id,
|
"hotel_id": hotel_id,
|
||||||
//"username": payload.username,
|
|
||||||
"exp": expiration
|
"exp": expiration
|
||||||
});
|
});
|
||||||
|
|
||||||
let token = match encode(
|
let token = match encode(&Header::default(), &claims, &keys.encoding) {
|
||||||
&Header::default(),
|
|
||||||
&claims,
|
|
||||||
&keys.encoding
|
|
||||||
) {
|
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "JWT creation failed").into_response(),
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "JWT creation failed").into_response(),
|
||||||
};
|
};
|
||||||
Json(LoginResponse { token }).into_response()
|
|
||||||
|
tokens.push(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if tokens.is_empty() {
|
||||||
|
return (StatusCode::UNAUTHORIZED, "Invalid or mismatched token").into_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Json(tokens).into_response()
|
||||||
|
Json(MultiLoginResponse { tokens }).into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn logout_from_single_device (
|
pub async fn logout_from_single_device (
|
||||||
|
|||||||
@@ -27,6 +27,17 @@ GOOD
|
|||||||
-p 8080:8080 `
|
-p 8080:8080 `
|
||||||
rust-api:1.0.1
|
rust-api:1.0.1
|
||||||
|
|
||||||
|
linux distro
|
||||||
|
|
||||||
|
docker run `
|
||||||
|
--name hotel-api `
|
||||||
|
-e JWT_SECRET=your_jwt_secret_key `
|
||||||
|
-v "/var/lib/hotel-api/db:/app/db" `
|
||||||
|
-p 8080:8080 `
|
||||||
|
rust-api:1.0.2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -81,3 +92,8 @@ GOOD
|
|||||||
"Propagation": "rprivate"
|
"Propagation": "rprivate"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user