Updating ui
This commit is contained in:
@@ -3,10 +3,9 @@ use std::env;
|
||||
use actix_web::{get, post, web, HttpResponse, ResponseError, cookie::{Cookie, time::Duration}, HttpRequest};
|
||||
use log::error;
|
||||
use redis::AsyncCommands;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use siren::ServiceError;
|
||||
|
||||
use crate::{auth::{InsertUser, JwtAuth, LoginRequest, QueryUser, RefreshToken, RegisterUser}, storage};
|
||||
use crate::{auth::{InsertUser, Auth, LoginRequest, QueryUser, RegisterUser, Session, SESSION_COOKIE_NAME}, storage::{self}};
|
||||
|
||||
use super::verify_hash;
|
||||
|
||||
@@ -47,14 +46,7 @@ async fn login(request: HttpRequest, login_request: web::Json<LoginRequest>) ->
|
||||
};
|
||||
// Verify password
|
||||
if verify_hash(&login_request.password, &query_user.hash) {
|
||||
let mut refresh_token = RefreshToken::new(&email, &ip_address);
|
||||
let access_token = match refresh_token.create_access_token() {
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
error!("Failed to generate access token: {}", err);
|
||||
return ResponseError::error_response(&err)
|
||||
}
|
||||
};
|
||||
let session = Session::new(&email, &ip_address);
|
||||
|
||||
let mut conn = match storage::redis_async_connection().await {
|
||||
Ok(conn) => conn,
|
||||
@@ -64,51 +56,33 @@ async fn login(request: HttpRequest, login_request: web::Json<LoginRequest>) ->
|
||||
}
|
||||
};
|
||||
|
||||
let access_token_max_age = env::var("ACCESS_TOKEN_MAXAGE")
|
||||
.expect("ACCESS_TOKEN_MAXAGE must be set")
|
||||
let session_ttl = env::var("SESSION_TTL")
|
||||
.expect("SESSION_TTL must be set")
|
||||
.parse::<i64>()
|
||||
.expect("ACCESS_TOKEN_MAXAGE must be an integer");
|
||||
.expect("SESSION_TTL must be an integer");
|
||||
|
||||
let refresh_token_max_age = env::var("REFRESH_TOKEN_MAXAGE")
|
||||
.expect("REFRESH_TOKEN_MAXAGE must be set")
|
||||
.parse::<i64>()
|
||||
.expect("REFRESH_TOKEN_MAXAGE must be an integer");
|
||||
|
||||
let access_result: redis::RedisResult<()> = conn.set_ex(access_token.id.to_string(), &serde_json::to_string(&access_token).unwrap(), (access_token_max_age * 60) as usize).await;
|
||||
if let Err(err) = access_result {
|
||||
let session_result: redis::RedisResult<()> = conn.set_ex(session.id.to_string(), &serde_json::to_string(&session).unwrap(), (session_ttl * 60) as usize).await;
|
||||
if let Err(err) = session_result {
|
||||
error!("Failed to set access token in redis: {}", err);
|
||||
return ResponseError::error_response(&ServiceError::from(err))
|
||||
};
|
||||
|
||||
let refresh_result: redis::RedisResult<()> = conn.set_ex(refresh_token.id.to_string(), &serde_json::to_string(&refresh_token).unwrap(), (refresh_token_max_age * 60) as usize).await;
|
||||
if let Err(err) = refresh_result {
|
||||
error!("Failed to set refresh token in redis: {}", err);
|
||||
return ResponseError::error_response(&ServiceError::from(err))
|
||||
};
|
||||
|
||||
let access_cookie = Cookie::build("access_token", access_token.token.clone().unwrap())
|
||||
let session_cookie = Cookie::build(SESSION_COOKIE_NAME, session.id.clone())
|
||||
.path("/")
|
||||
.max_age(Duration::new(access_token_max_age * 60, 0))
|
||||
.max_age(Duration::new(session_ttl * 60, 0))
|
||||
.http_only(true)
|
||||
.secure(true)
|
||||
.finish();
|
||||
let refresh_cookie = Cookie::build("refresh_token", refresh_token.id.clone())
|
||||
let user_id_cookie = Cookie::build("user_id", session.user_id.clone())
|
||||
.path("/")
|
||||
.max_age(Duration::new(refresh_token_max_age * 60, 0))
|
||||
.http_only(true)
|
||||
.secure(true)
|
||||
.finish();
|
||||
let logged_in_cookie = Cookie::build("logged_in", "true")
|
||||
.path("/")
|
||||
.max_age(Duration::new(access_token_max_age * 60, 0))
|
||||
.max_age(Duration::new(session_ttl * 60, 0))
|
||||
.http_only(false)
|
||||
.finish();
|
||||
|
||||
HttpResponse::Ok()
|
||||
.cookie(access_cookie)
|
||||
.cookie(refresh_cookie)
|
||||
.cookie(logged_in_cookie)
|
||||
.json(JwtAuth { id: access_token.id, user: query_user.into() })
|
||||
.cookie(session_cookie)
|
||||
.cookie(user_id_cookie)
|
||||
.json(Auth { id: session.id, user: query_user.into() })
|
||||
} else {
|
||||
return ResponseError::error_response(&ServiceError {
|
||||
status: 401,
|
||||
@@ -117,29 +91,9 @@ async fn login(request: HttpRequest, login_request: web::Json<LoginRequest>) ->
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct RefreshParams {
|
||||
refresh_token_rotation: Option<bool>
|
||||
}
|
||||
|
||||
#[get("/refresh")]
|
||||
async fn refresh(req: HttpRequest) -> HttpResponse {
|
||||
async fn refresh(req: HttpRequest, auth: Auth) -> HttpResponse {
|
||||
let ip_address = req.peer_addr().unwrap().ip().to_string();
|
||||
let params = match web::Query::<RefreshParams>::from_query(req.query_string()) {
|
||||
Ok(params) => params,
|
||||
Err(err) => return ResponseError::error_response(&ServiceError {
|
||||
status: 422,
|
||||
message: err.to_string()
|
||||
})
|
||||
};
|
||||
|
||||
let refresh_token_string = match req.cookie("refresh_token") {
|
||||
Some(cookie) => cookie.value().to_string(),
|
||||
None => return ResponseError::error_response(&ServiceError {
|
||||
status: 401,
|
||||
message: "Refresh token not found".to_string()
|
||||
})
|
||||
};
|
||||
|
||||
let mut conn = match storage::redis_async_connection().await {
|
||||
Ok(conn) => conn,
|
||||
@@ -149,129 +103,38 @@ async fn refresh(req: HttpRequest) -> HttpResponse {
|
||||
}
|
||||
};
|
||||
|
||||
let mut refresh_token: RefreshToken = match conn.get::<_, String>(refresh_token_string.clone()).await {
|
||||
Ok(result) => match serde_json::from_str::<RefreshToken>(&result) {
|
||||
Ok(result) => {
|
||||
if verify_hash(&ip_address, &result.ip_address) {
|
||||
result
|
||||
} else {
|
||||
return ResponseError::error_response(&ServiceError {
|
||||
status: 401,
|
||||
message: "Refresh token is invalid".to_string()
|
||||
})
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
error!("Failed to deserialize refresh token: {}", err);
|
||||
return ResponseError::error_response(&ServiceError::from(err))
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
error!("Failed to get refresh token from redis: {}", err);
|
||||
return ResponseError::error_response(&ServiceError::from(err))
|
||||
}
|
||||
let session_ttl = env::var("SESSION_TTL")
|
||||
.expect("SESSION_TTL must be set")
|
||||
.parse::<i64>()
|
||||
.expect("SESSION_TTL must be an integer");
|
||||
|
||||
// Delete old session
|
||||
let _: redis::RedisResult<()> = conn.del(auth.id).await;
|
||||
|
||||
// Create new session
|
||||
let session = Session::new(&auth.user.email, &ip_address);
|
||||
let session_result: redis::RedisResult<()> = conn.set_ex(session.id.to_string(), &serde_json::to_string(&session).unwrap(), (session_ttl * 60) as usize).await;
|
||||
if let Err(err) = session_result {
|
||||
error!("Failed to set session id in redis: {}", err);
|
||||
return ResponseError::error_response(&ServiceError::from(err))
|
||||
};
|
||||
|
||||
let email = refresh_token.email.clone();
|
||||
// Create cookies
|
||||
let session_cookie = session.create_cookie();
|
||||
let user_id_cookie = Cookie::build("user_id", session.user_id.clone())
|
||||
.path("/")
|
||||
.max_age(Duration::new(session_ttl * 60, 0))
|
||||
.http_only(false)
|
||||
.finish();
|
||||
|
||||
match QueryUser::get_by_email(&email) {
|
||||
Ok(query_user) => {
|
||||
// Revoke all old access tokens
|
||||
for id in refresh_token.tokens {
|
||||
let _: redis::RedisResult<()> = conn.del(id).await;
|
||||
}
|
||||
refresh_token.tokens = vec![];
|
||||
|
||||
// Create new access token
|
||||
let access_token = match refresh_token.create_access_token() {
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
error!("Failed to generate access token: {}", err);
|
||||
return ResponseError::error_response(&err)
|
||||
}
|
||||
};
|
||||
|
||||
let access_token_max_age = env::var("ACCESS_TOKEN_MAXAGE")
|
||||
.expect("ACCESS_TOKEN_MAXAGE must be set")
|
||||
.parse::<i64>()
|
||||
.expect("ACCESS_TOKEN_MAXAGE must be an integer");
|
||||
|
||||
let access_result: redis::RedisResult<()> = conn.set_ex(access_token.id.to_string(), &serde_json::to_string(&access_token).unwrap(), (access_token_max_age * 60) as usize).await;
|
||||
if let Err(err) = access_result {
|
||||
error!("Failed to set access token in redis: {}", err);
|
||||
return ResponseError::error_response(&ServiceError::from(err))
|
||||
};
|
||||
|
||||
let access_cookie = Cookie::build("access_token", access_token.id.clone())
|
||||
.path("/")
|
||||
.max_age(Duration::new(access_token_max_age * 60, 0))
|
||||
.http_only(true)
|
||||
.secure(true)
|
||||
.finish();
|
||||
let logged_in_cookie = Cookie::build("logged_in", "true")
|
||||
.path("/")
|
||||
.max_age(Duration::new(access_token_max_age * 60, 0))
|
||||
.http_only(false)
|
||||
.finish();
|
||||
|
||||
// Refresh the refresh token if requested
|
||||
let refresh_token_rotation = match params.refresh_token_rotation {
|
||||
Some(refresh_token_rotation) => refresh_token_rotation,
|
||||
None => false
|
||||
};
|
||||
if refresh_token_rotation {
|
||||
// Delete the old refresh token from redis
|
||||
let _: redis::RedisResult<()> = conn.del(refresh_token.id.to_string()).await;
|
||||
|
||||
let refresh_token = RefreshToken::new(&refresh_token.email, &ip_address);
|
||||
let refresh_token_max_age = env::var("REFRESH_TOKEN_MAXAGE")
|
||||
.expect("REFRESH_TOKEN_MAXAGE must be set")
|
||||
.parse::<i64>()
|
||||
.expect("REFRESH_TOKEN_MAXAGE must be an integer");
|
||||
|
||||
// Add the new refresh token to redis
|
||||
let refresh_result: redis::RedisResult<()> = conn.set_ex(refresh_token.id.to_string(), &serde_json::to_string(&refresh_token).unwrap(), (refresh_token_max_age * 60) as usize).await;
|
||||
if let Err(err) = refresh_result {
|
||||
error!("Failed to set refresh token in redis: {}", err);
|
||||
return ResponseError::error_response(&ServiceError {
|
||||
status: 500,
|
||||
message: format!("Failed to set refresh token in redis: {}", err)
|
||||
})
|
||||
};
|
||||
|
||||
let refresh_cookie = Cookie::build("refresh_token", refresh_token.id.clone())
|
||||
.path("/")
|
||||
.max_age(Duration::new(refresh_token_max_age * 60, 0))
|
||||
.http_only(true)
|
||||
.secure(true)
|
||||
.finish();
|
||||
|
||||
HttpResponse::Ok()
|
||||
.cookie(refresh_cookie)
|
||||
.cookie(access_cookie)
|
||||
.cookie(logged_in_cookie)
|
||||
.json(JwtAuth { id: access_token.id, user: query_user.into() })
|
||||
} else {
|
||||
HttpResponse::Ok()
|
||||
.cookie(access_cookie)
|
||||
.cookie(logged_in_cookie)
|
||||
.json(JwtAuth { id: access_token.id, user: query_user.into() })
|
||||
}
|
||||
},
|
||||
Err(err) => return ResponseError::error_response(&err)
|
||||
}
|
||||
HttpResponse::Ok()
|
||||
.cookie(session_cookie)
|
||||
.cookie(user_id_cookie)
|
||||
.json(Auth { id: session.id, user: auth.user })
|
||||
}
|
||||
|
||||
#[post("/logout")]
|
||||
async fn logout(req: HttpRequest, auth: JwtAuth) -> HttpResponse {
|
||||
let refresh_token = match req.cookie("refresh_token") {
|
||||
Some(cookie) => cookie.value().to_string(),
|
||||
None => return ResponseError::error_response(&ServiceError {
|
||||
status: 401,
|
||||
message: "Refresh token not found".to_string()
|
||||
})
|
||||
};
|
||||
|
||||
async fn logout(auth: Auth) -> HttpResponse {
|
||||
let mut conn = match storage::redis_async_connection().await {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => {
|
||||
@@ -280,45 +143,32 @@ async fn logout(req: HttpRequest, auth: JwtAuth) -> HttpResponse {
|
||||
}
|
||||
};
|
||||
|
||||
let access_result: redis::RedisResult<()> = conn.del(&[
|
||||
refresh_token.to_string(),
|
||||
auth.id.to_string()
|
||||
]).await;
|
||||
if let Err(err) = access_result {
|
||||
error!("Failed to set access token in redis: {}", err);
|
||||
return ResponseError::error_response(&ServiceError {
|
||||
status: 500,
|
||||
message: format!("Failed to set access token in redis: {}", err)
|
||||
})
|
||||
let session_result: redis::RedisResult<()> = conn.del(&auth.id.to_string()).await;
|
||||
if let Err(err) = session_result {
|
||||
error!("Failed to remove session id in redis: {}", err);
|
||||
return ResponseError::error_response(&ServiceError::from(err))
|
||||
};
|
||||
|
||||
let access_cookie = Cookie::build("access_token", "")
|
||||
let session_cookie = Cookie::build(SESSION_COOKIE_NAME, "")
|
||||
.path("/")
|
||||
.max_age(Duration::new(-1, 0))
|
||||
.secure(true)
|
||||
.http_only(true)
|
||||
.finish();
|
||||
let refresh_cookie = Cookie::build("refresh_token", "")
|
||||
.path("/")
|
||||
.max_age(Duration::new(-1, 0))
|
||||
.secure(true)
|
||||
.http_only(true)
|
||||
.finish();
|
||||
let logged_in_cookie = Cookie::build("logged_in", "")
|
||||
let user_id_cookie = Cookie::build("user_id", "")
|
||||
.path("/")
|
||||
.max_age(Duration::new(-1, 0))
|
||||
.http_only(true)
|
||||
.finish();
|
||||
|
||||
HttpResponse::Ok()
|
||||
.cookie(access_cookie)
|
||||
.cookie(refresh_cookie)
|
||||
.cookie(logged_in_cookie)
|
||||
.cookie(session_cookie)
|
||||
.cookie(user_id_cookie)
|
||||
.finish()
|
||||
}
|
||||
|
||||
#[get("/me")]
|
||||
async fn me(auth: JwtAuth) -> HttpResponse {
|
||||
async fn me(auth: Auth) -> HttpResponse {
|
||||
HttpResponse::Ok().json(auth)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user