mirror of
https://github.com/danbulant/Portfolio
synced 2026-07-05 11:00:56 +00:00
feat: candidate refresh token auth
This commit is contained in:
parent
69b5a4fabf
commit
82f9098ed5
5 changed files with 69 additions and 5 deletions
27
api/src/guard/candidate_refresh_token.rs
Normal file
27
api/src/guard/candidate_refresh_token.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
use portfolio_core::sea_orm::prelude::Uuid;
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::outcome::Outcome;
|
||||||
|
use rocket::request::{FromRequest, Request};
|
||||||
|
|
||||||
|
|
||||||
|
pub struct UUIDCookie(Uuid);
|
||||||
|
|
||||||
|
impl UUIDCookie {
|
||||||
|
pub fn value(self) -> Uuid {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for UUIDCookie {
|
||||||
|
type Error = Status;
|
||||||
|
async fn from_request(req: &'r Request<'_>) -> Outcome<UUIDCookie, (Status, Status), ()> {
|
||||||
|
let session_id = req.cookies().get("id").unwrap().name_value().1;
|
||||||
|
println!("session_id: {}", session_id);
|
||||||
|
|
||||||
|
match Uuid::parse_str(&session_id) {
|
||||||
|
Ok(uuid) => Outcome::Success(UUIDCookie(uuid)),
|
||||||
|
Err(_) => return Outcome::Failure((Status::BadRequest, Status::BadRequest)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
pub mod candidate_jwt;
|
pub mod candidate_jwt;
|
||||||
|
pub mod candidate_refresh_token;
|
||||||
|
|
@ -28,6 +28,8 @@ pub use entity::candidate::Entity as Candidate;
|
||||||
|
|
||||||
use portfolio_core::crypto::random_8_char_string;
|
use portfolio_core::crypto::random_8_char_string;
|
||||||
|
|
||||||
|
use crate::guard::candidate_refresh_token::UUIDCookie;
|
||||||
|
|
||||||
fn custom_err_from_service_err(service_err: ServiceError) -> Custom<String> {
|
fn custom_err_from_service_err(service_err: ServiceError) -> Custom<String> {
|
||||||
Custom(Status::from_code(service_err.0.code).unwrap_or_default(), service_err.1.to_string())
|
Custom(Status::from_code(service_err.0.code).unwrap_or_default(), service_err.1.to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -54,7 +56,18 @@ async fn refresh_token(conn: Connection<'_, Db>, token_req: Result<TokenRequest,
|
||||||
let refresh_token = SessionService::new_refresh_token(db, jwt.application_id).await;
|
let refresh_token = SessionService::new_refresh_token(db, jwt.application_id).await;
|
||||||
|
|
||||||
Ok(refresh_token.ok().unwrap())
|
Ok(refresh_token.ok().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/validate_refresh")]
|
||||||
|
async fn validate(conn: Connection<'_, Db>, uuid_cookie: Result<UUIDCookie, Status>) -> Result<String, Custom<String>> {
|
||||||
|
let db = conn.into_inner();
|
||||||
|
let user = CandidateService::auth_user_session(db, uuid_cookie.ok().unwrap().value()).await;
|
||||||
|
|
||||||
|
|
||||||
|
match user {
|
||||||
|
Ok(user) => Ok(user.application.to_string()),
|
||||||
|
Err(err) => Err(custom_err_from_service_err(err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/login", data = "<login_form>")]
|
#[post("/login", data = "<login_form>")]
|
||||||
|
|
@ -112,7 +125,7 @@ async fn start() -> Result<(), rocket::Error> {
|
||||||
.attach(Db::init())
|
.attach(Db::init())
|
||||||
.attach(AdHoc::try_on_ignite("Migrations", run_migrations))
|
.attach(AdHoc::try_on_ignite("Migrations", run_migrations))
|
||||||
//.mount("/", FileServer::from(relative!("/static")))
|
//.mount("/", FileServer::from(relative!("/static")))
|
||||||
.mount("/", routes![create, login, hello, whoami, refresh_token])
|
.mount("/", routes![create, login, hello, whoami, refresh_token, validate])
|
||||||
.register("/", catchers![])
|
.register("/", catchers![])
|
||||||
.launch()
|
.launch()
|
||||||
.await
|
.await
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ pub const USER_NOT_FOUND_ERROR: ServiceError = ServiceError(Status { code: 404 }
|
||||||
pub const DB_ERROR: ServiceError = ServiceError(Status { code: 500 },
|
pub const DB_ERROR: ServiceError = ServiceError(Status { code: 500 },
|
||||||
"Database error");
|
"Database error");
|
||||||
|
|
||||||
pub const USER_NOT_FOUND_BY_JWT_ID: ServiceError = ServiceError(Status { code: 500 }, // User got somehow
|
pub const USER_NOT_FOUND_BY_JWT_ID: ServiceError = ServiceError(Status { code: 500 }, // User got somehow deleted
|
||||||
|
"User not found, please contact technical support"); // Shouldn't ever happen
|
||||||
|
|
||||||
|
pub const USER_NOT_FOUND_BY_SESSION_ID: ServiceError = ServiceError(Status { code: 500 }, // User got somehow deleted
|
||||||
"User not found, please contact technical support"); // Shouldn't ever happen
|
"User not found, please contact technical support"); // Shouldn't ever happen
|
||||||
pub struct ServiceError<'a>(pub Status, pub &'a str);
|
pub struct ServiceError<'a>(pub Status, pub &'a str);
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use entity::candidate;
|
use entity::candidate;
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::{DatabaseConnection, prelude::Uuid, ModelTrait};
|
||||||
|
|
||||||
use crate::{crypto, Query, token::{generate_candidate_token, candidate_token::CandidateToken}, error::{ServiceError, USER_NOT_FOUND_ERROR, INVALID_CREDENTIALS_ERROR, DB_ERROR, USER_NOT_FOUND_BY_JWT_ID}};
|
use crate::{crypto, Query, token::{generate_candidate_token, candidate_token::CandidateToken}, error::{ServiceError, USER_NOT_FOUND_ERROR, INVALID_CREDENTIALS_ERROR, DB_ERROR, USER_NOT_FOUND_BY_JWT_ID, USER_NOT_FOUND_BY_SESSION_ID}};
|
||||||
|
|
||||||
pub struct CandidateService;
|
pub struct CandidateService;
|
||||||
|
|
||||||
|
|
@ -40,4 +40,24 @@ impl CandidateService {
|
||||||
|
|
||||||
Ok(candidate)
|
Ok(candidate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn auth_user_session(db: &DatabaseConnection, uuid: Uuid) -> Result<candidate::Model, ServiceError> {
|
||||||
|
let session = match Query::find_session_by_uuid(db, uuid).await {
|
||||||
|
Ok(session) => match session {
|
||||||
|
Some(session) => session,
|
||||||
|
None => return Err(USER_NOT_FOUND_BY_SESSION_ID)
|
||||||
|
},
|
||||||
|
Err(_) => {return Err(DB_ERROR)}
|
||||||
|
};
|
||||||
|
|
||||||
|
let candidate = match session.find_related(candidate::Entity).one(db).await {
|
||||||
|
Ok(candidate) => match candidate {
|
||||||
|
Some(candidate) => candidate,
|
||||||
|
None => return Err(USER_NOT_FOUND_BY_JWT_ID)
|
||||||
|
},
|
||||||
|
Err(_) => {return Err(DB_ERROR)}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(candidate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue