mirror of
https://github.com/danbulant/Portfolio
synced 2026-05-27 14:02:14 +00:00
feat: get refresh token
This commit is contained in:
parent
a58cf73acc
commit
9378dcbead
5 changed files with 86 additions and 5 deletions
|
|
@ -4,6 +4,7 @@ extern crate rocket;
|
||||||
use guard::candidate_jwt::TokenRequest;
|
use guard::candidate_jwt::TokenRequest;
|
||||||
use portfolio_core::error::ServiceError;
|
use portfolio_core::error::ServiceError;
|
||||||
use portfolio_core::services::candidate_service::CandidateService;
|
use portfolio_core::services::candidate_service::CandidateService;
|
||||||
|
use portfolio_core::services::session_service::SessionService;
|
||||||
use requests::LoginRequest;
|
use requests::LoginRequest;
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::{Rocket, Build};
|
use rocket::{Rocket, Build};
|
||||||
|
|
@ -45,6 +46,17 @@ async fn create(conn: Connection<'_, Db>, post_form: Json<candidate::Model>) ->
|
||||||
Ok(plain_text_password)
|
Ok(plain_text_password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/refresh")]
|
||||||
|
async fn refresh_token(conn: Connection<'_, Db>, token_req: Result<TokenRequest, Status>) -> Result<String, Custom<String>> {
|
||||||
|
let db = conn.into_inner();
|
||||||
|
let jwt = token_req.ok().unwrap().to_token();
|
||||||
|
|
||||||
|
let refresh_token = SessionService::new_refresh_token(db, jwt.application_id).await;
|
||||||
|
|
||||||
|
Ok(refresh_token.ok().unwrap())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#[post("/login", data = "<login_form>")]
|
#[post("/login", data = "<login_form>")]
|
||||||
async fn login(conn: Connection<'_, Db>, login_form: Json<LoginRequest>) -> Result<String, Custom<String>> {
|
async fn login(conn: Connection<'_, Db>, login_form: Json<LoginRequest>) -> Result<String, Custom<String>> {
|
||||||
let db = conn.into_inner();
|
let db = conn.into_inner();
|
||||||
|
|
@ -72,7 +84,13 @@ async fn whoami(conn: Connection<'_, Db>, token_req: Result<TokenRequest, Status
|
||||||
let user = CandidateService::authenticate_candidate(db, token).await;
|
let user = CandidateService::authenticate_candidate(db, token).await;
|
||||||
|
|
||||||
match user {
|
match user {
|
||||||
Ok(user) => Ok(format!("{} {}", user.name.unwrap(), user.surname.unwrap())),
|
Ok(user) => Ok(
|
||||||
|
format!("{} {} {}",
|
||||||
|
user.application,
|
||||||
|
user.name.unwrap_or("".to_owned()),
|
||||||
|
user.surname.unwrap_or("".to_owned())
|
||||||
|
)
|
||||||
|
),
|
||||||
Err(e) => Err(custom_err_from_service_err(e)),
|
Err(e) => Err(custom_err_from_service_err(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -94,7 +112,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])
|
.mount("/", routes![create, login, hello, whoami, refresh_token])
|
||||||
.register("/", catchers![])
|
.register("/", catchers![])
|
||||||
.launch()
|
.launch()
|
||||||
.await
|
.await
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use argon2::{
|
||||||
Argon2, PasswordHasher as ArgonPasswordHasher, PasswordVerifier as ArgonPasswordVerifier,
|
Argon2, PasswordHasher as ArgonPasswordHasher, PasswordVerifier as ArgonPasswordVerifier,
|
||||||
};
|
};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
use sha2::{Sha256, Digest};
|
||||||
|
|
||||||
|
|
||||||
/// Foolproof random 8 char string
|
/// Foolproof random 8 char string
|
||||||
|
|
@ -28,6 +29,14 @@ pub fn random_8_char_string() -> String {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hash_sha256(s: String) -> String {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(s);
|
||||||
|
let result = hasher.finalize();
|
||||||
|
format!("{result:x}")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
pub fn hash_password(password_plaint_text: &str) -> Result<String, argon2::password_hash::Error> {
|
pub fn hash_password(password_plaint_text: &str) -> Result<String, argon2::password_hash::Error> {
|
||||||
let password = password_plaint_text.as_bytes();
|
let password = password_plaint_text.as_bytes();
|
||||||
let salt = "c2VjcmV0bHl0ZXN0aW5nZXZlcnl0aGluZw";
|
let salt = "c2VjcmV0bHl0ZXN0aW5nZXZlcnl0aGluZw";
|
||||||
|
|
@ -51,3 +60,10 @@ pub fn verify_password(
|
||||||
.verify_password(password_plaint_text.as_bytes(), &parsed_hash)
|
.verify_password(password_plaint_text.as_bytes(), &parsed_hash)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn verify_password_test() {
|
||||||
|
let password = "test";
|
||||||
|
let hash = hash_password(password).unwrap();
|
||||||
|
assert!(verify_password(password, &hash).unwrap());
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use ::entity::{candidate};
|
use chrono::Utc;
|
||||||
use sea_orm::*;
|
use ::entity::{candidate, session};
|
||||||
|
use sea_orm::{*, prelude::Uuid};
|
||||||
use crate::crypto::hash_password;
|
use crate::crypto::hash_password;
|
||||||
|
|
||||||
pub struct Mutation;
|
pub struct Mutation;
|
||||||
|
|
@ -24,4 +25,22 @@ impl Mutation {
|
||||||
.insert(db)
|
.insert(db)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn insert_session(
|
||||||
|
db: &DbConn,
|
||||||
|
user_id: i32,
|
||||||
|
random_uuid: Uuid,
|
||||||
|
hashed_jwt: String
|
||||||
|
) -> Result<session::Model, DbErr> {
|
||||||
|
session::ActiveModel {
|
||||||
|
id: Set(random_uuid),
|
||||||
|
hashed_token: Set(hashed_jwt),
|
||||||
|
user_id: Set(user_id),
|
||||||
|
created_at: Set(Utc::now().naive_local()),
|
||||||
|
updated_at: Set(Utc::now().naive_local()),
|
||||||
|
}
|
||||||
|
.insert(db)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
pub mod candidate_service;
|
pub mod candidate_service;
|
||||||
|
pub mod session_service;
|
||||||
27
core/src/services/session_service.rs
Normal file
27
core/src/services/session_service.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
use sea_orm::{prelude::Uuid, DbConn};
|
||||||
|
|
||||||
|
use crate::{token::{generate_candidate_token}, Query, error::{USER_NOT_FOUND_ERROR, DB_ERROR, ServiceError}, crypto::hash_sha256, Mutation};
|
||||||
|
|
||||||
|
pub struct SessionService;
|
||||||
|
|
||||||
|
impl SessionService {
|
||||||
|
pub async fn new_refresh_token(db: &DbConn, id: i32) -> Result<String, ServiceError> {
|
||||||
|
let candidate = match Query::find_candidate_by_id(db, id).await {
|
||||||
|
Ok(candidate) => match candidate {
|
||||||
|
Some(candidate) => candidate,
|
||||||
|
None => return Err(USER_NOT_FOUND_ERROR)
|
||||||
|
},
|
||||||
|
Err(_) => {return Err(DB_ERROR)}
|
||||||
|
};
|
||||||
|
let random_uuid: Uuid = Uuid::new_v4();
|
||||||
|
|
||||||
|
let jwt = generate_candidate_token(candidate);
|
||||||
|
|
||||||
|
let session = match Mutation::insert_session(db, id, random_uuid, hash_sha256(jwt)).await {
|
||||||
|
Ok(session) => session,
|
||||||
|
Err(_) => return Err(DB_ERROR)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(session.id.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue