Started adding doc to better build the system
This commit is contained in:
146
src/users.rs
146
src/users.rs
@@ -1,3 +1,29 @@
|
||||
//! # Authentication flow
|
||||
//! Before being able to authenticate, the user needs to create an account
|
||||
//! Here's how it can be done:
|
||||
//! - The user submits information on the client
|
||||
//! - The client sends a request to the "register" endpoint
|
||||
//! - The endpoint ensures the information given is valid
|
||||
//! - An account is created
|
||||
//! - The user is redirected to the login page
|
||||
//!
|
||||
//! After, the user can authenticate
|
||||
//! Here's how it works:
|
||||
//! - The users submits information on the client
|
||||
//! - The client sends a request to the "login" endpoint
|
||||
//! - The endpoint makes sure the information given is valid (otherwise reject the request: no db call)
|
||||
//! - Then it checks that the user exists
|
||||
//! - Then it checks that the correct password was given
|
||||
//! - Then it creates two tokens: a short-lived and a refresh-token
|
||||
//! - The short-lived token must be saved in local-storage and the refresh-token must be saved as a http only cookie
|
||||
//!
|
||||
//! Once authenticated, the short-lived token can be renewed
|
||||
//! Here's how it works:
|
||||
//! - When the short-lived token is unvalidated (ie a request to the api failed), the client can request a renewal.
|
||||
//! - The renewal request contains the now unvalid token with the refresh-token cookie.
|
||||
//! - The server checks that the refresh-token is valid (good user, not expired, ...)
|
||||
//! - If checks pass, the server generates a new short-lived token.
|
||||
//! - If it fails, the client redirects the user to login.
|
||||
use crate::AppState;
|
||||
use crate::types::{User, UserLogin, UserRegister, UserTokenClaims};
|
||||
use actix_web::web::{Data, Json};
|
||||
@@ -37,8 +63,8 @@ async fn login(
|
||||
"SELECT * FROM users WHERE username = $1",
|
||||
user_login.username
|
||||
)
|
||||
.fetch_optional(&app_state.database)
|
||||
.await?
|
||||
.fetch_optional(&app_state.database)
|
||||
.await?
|
||||
{
|
||||
Some(user) => {
|
||||
let hash = PasswordHash::new(&user.hash)?;
|
||||
@@ -81,36 +107,37 @@ async fn logout(
|
||||
req: HttpRequest,
|
||||
app_state: Data<AppState>,
|
||||
) -> Result<impl Responder, Box<(dyn Error + 'static)>> {
|
||||
todo!();
|
||||
// Put the (KeyId, User) pair in the revoked table
|
||||
// And remove data from client
|
||||
match req.headers().get("Authorization") {
|
||||
Some(token) => {
|
||||
let token = token.to_str()?;
|
||||
let token = match token.split_once(" ") {
|
||||
Some((_, token)) => token,
|
||||
None => return Ok(HttpResponse::BadRequest().finish()),
|
||||
};
|
||||
let mut key = File::open("pub.pem")?;
|
||||
let mut buf = vec![];
|
||||
key.read_to_end(&mut buf)?;
|
||||
let token = decode::<UserTokenClaims>(
|
||||
token,
|
||||
&DecodingKey::from_ec_pem(&buf).unwrap(),
|
||||
&Validation::new(Algorithm::ES256),
|
||||
)?;
|
||||
let exp = token.claims.exp as i64;
|
||||
query!(
|
||||
"INSERT INTO revoked ( token_id, user_id, expires ) VALUES ( $1, $2, $3 )",
|
||||
token.claims.kid,
|
||||
token.claims.uid,
|
||||
exp
|
||||
)
|
||||
.execute(&app_state.database)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
None => Ok(HttpResponse::BadRequest().finish()),
|
||||
}
|
||||
// match req.headers().get("Authorization") {
|
||||
// Some(token) => {
|
||||
// let token = token.to_str()?;
|
||||
// let token = match token.split_once(" ") {
|
||||
// Some((_, token)) => token,
|
||||
// None => return Ok(HttpResponse::BadRequest().finish()),
|
||||
// };
|
||||
// let mut key = File::open("pub.pem")?;
|
||||
// let mut buf = vec![];
|
||||
// key.read_to_end(&mut buf)?;
|
||||
// let token = decode::<UserTokenClaims>(
|
||||
// token,
|
||||
// &DecodingKey::from_ec_pem(&buf).unwrap(),
|
||||
// &Validation::new(Algorithm::ES256),
|
||||
// )?;
|
||||
// let exp = token.claims.exp as i64;
|
||||
// query!(
|
||||
// "INSERT INTO revoked ( token_id, user_id, expires ) VALUES ( $1, $2, $3 )",
|
||||
// token.claims.kid,
|
||||
// token.claims.uid,
|
||||
// exp
|
||||
// )
|
||||
// .execute(&app_state.database)
|
||||
// .await?;
|
||||
// Ok(HttpResponse::Ok().finish())
|
||||
// }
|
||||
// None => Ok(HttpResponse::BadRequest().finish()),
|
||||
// }
|
||||
}
|
||||
|
||||
#[post("/register")]
|
||||
@@ -138,8 +165,8 @@ async fn register(
|
||||
hash,
|
||||
user_register.email
|
||||
)
|
||||
.execute(&app_state.database)
|
||||
.await?;
|
||||
.execute(&app_state.database)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
|
||||
@@ -147,31 +174,32 @@ async fn verify_token(
|
||||
app_state: Data<AppState>,
|
||||
token: &str,
|
||||
) -> Result<bool, Box<(dyn Error + 'static)>> {
|
||||
let mut key = File::open("pub.pem")?;
|
||||
let mut buf = vec![];
|
||||
key.read_to_end(&mut buf)?;
|
||||
let token = decode::<UserTokenClaims>(
|
||||
token,
|
||||
&DecodingKey::from_ec_pem(&buf).unwrap(),
|
||||
&Validation::new(Algorithm::ES256),
|
||||
)?;
|
||||
let exp = token.claims.exp as u64;
|
||||
let now = get_current_timestamp();
|
||||
if exp > now {
|
||||
return Ok(false);
|
||||
}
|
||||
let kid = token.claims.kid;
|
||||
let uid = token.claims.uid;
|
||||
if query!(
|
||||
"SELECT token_id FROM revoked WHERE token_id = $1 AND user_id = $2",
|
||||
kid,
|
||||
uid
|
||||
)
|
||||
.fetch_optional(&app_state.database)
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
Ok(true)
|
||||
todo!();
|
||||
// let mut key = File::open("pub.pem")?;
|
||||
// let mut buf = vec![];
|
||||
// key.read_to_end(&mut buf)?;
|
||||
// let token = decode::<UserTokenClaims>(
|
||||
// token,
|
||||
// &DecodingKey::from_ec_pem(&buf).unwrap(),
|
||||
// &Validation::new(Algorithm::ES256),
|
||||
// )?;
|
||||
// let exp = token.claims.exp as u64;
|
||||
// let now = get_current_timestamp();
|
||||
// if exp > now {
|
||||
// return Ok(false);
|
||||
// }
|
||||
// let kid = token.claims.kid;
|
||||
// let uid = token.claims.uid;
|
||||
// if query!(
|
||||
// "SELECT token_id FROM revoked WHERE token_id = $1 AND user_id = $2",
|
||||
// kid,
|
||||
// uid
|
||||
// )
|
||||
// .fetch_optional(&app_state.database)
|
||||
// .await?
|
||||
// .is_some()
|
||||
// {
|
||||
// return Ok(false);
|
||||
// }
|
||||
// Ok(true)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user