Added logout api route
This commit is contained in:
66
src/users.rs
66
src/users.rs
@@ -30,12 +30,8 @@ use actix_web::cookie::time::Duration;
|
||||
use actix_web::web::{Data, Json};
|
||||
use actix_web::{HttpRequest, HttpResponse, Responder, post};
|
||||
use argon2::Argon2;
|
||||
use argon2::password_hash::{
|
||||
PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng,
|
||||
};
|
||||
use jsonwebtoken::{
|
||||
Algorithm, DecodingKey, EncodingKey, Header, Validation, decode, encode, get_current_timestamp,
|
||||
};
|
||||
use argon2::password_hash::{PasswordHash, PasswordVerifier};
|
||||
use jsonwebtoken::{Algorithm, EncodingKey, Header, encode, get_current_timestamp};
|
||||
use rand::Rng;
|
||||
use rand::distr::Alphanumeric;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
@@ -45,14 +41,7 @@ use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use uuid::Uuid;
|
||||
/*
|
||||
Will use cookies in final version of app
|
||||
In the meanwhile, we'll just save the token in the userStore
|
||||
|
||||
use actix_web::cookie::{Cookie, SameSite};
|
||||
use actix_web::cookie::time::{Duration, OffsetDateTime, UtcDateTime};
|
||||
*/
|
||||
use sqlx::sqlite::SqliteQueryResult;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Password {
|
||||
@@ -77,14 +66,16 @@ impl User {
|
||||
async fn fetch_optional(
|
||||
database: &Pool<Sqlite>,
|
||||
id: Option<i64>,
|
||||
uuid: Option<&str>,
|
||||
username: Option<&Username>,
|
||||
) -> Result<Option<User>, sqlx::Error> {
|
||||
match username {
|
||||
Some(username) => {
|
||||
query_as!(
|
||||
User,
|
||||
"SELECT * FROM users WHERE 'id' = ?1 OR 'username' = ?2",
|
||||
"SELECT * FROM users WHERE 'id' = ?1 OR 'username' = ?2 or 'uuid' = ?3",
|
||||
id,
|
||||
uuid,
|
||||
username.value
|
||||
)
|
||||
.fetch_optional(database)
|
||||
@@ -93,8 +84,9 @@ impl User {
|
||||
None => {
|
||||
query_as!(
|
||||
User,
|
||||
"SELECT * FROM users WHERE 'id' = ?1 OR 'username' = ?2",
|
||||
"SELECT * FROM users WHERE 'id' = ?1 OR 'username' = ?2 OR 'uuid' = ?3",
|
||||
id,
|
||||
uuid,
|
||||
None::<String>
|
||||
)
|
||||
.fetch_optional(database)
|
||||
@@ -187,7 +179,7 @@ async fn login(
|
||||
login_info: Json<LoginInfo>,
|
||||
) -> Result<impl Responder, Box<dyn Error>> {
|
||||
if let Ok(Some(user)) =
|
||||
User::fetch_optional(&app_state.database, None, Some(&login_info.username)).await
|
||||
User::fetch_optional(&app_state.database, None, None, Some(&login_info.username)).await
|
||||
{
|
||||
if validate_authentication(&user, &login_info.password)? {
|
||||
let jwt_token = generate_jwt(&user, app_state.clone())?;
|
||||
@@ -213,6 +205,46 @@ async fn login(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct LogoutInfo {
|
||||
uuid: String,
|
||||
token: String,
|
||||
}
|
||||
|
||||
#[post("/logout")]
|
||||
async fn logout(
|
||||
app_state: Data<AppState>,
|
||||
logout_info: Json<LogoutInfo>,
|
||||
req: HttpRequest,
|
||||
) -> Result<impl Responder, Box<dyn Error>> {
|
||||
let refresh_cookie = match req.cookie("refresh_token") {
|
||||
Some(cookie) => cookie,
|
||||
None => return Ok(HttpResponse::Unauthorized().finish()),
|
||||
};
|
||||
let refresh_token = refresh_cookie.value();
|
||||
match User::fetch_optional(&app_state.database, None, Some(&logout_info.uuid), None).await? {
|
||||
Some(user) => {
|
||||
let now = get_current_timestamp() as i64;
|
||||
let result: SqliteQueryResult = query!(
|
||||
"UPDATE refresh_tokens SET expiry = ?1 WHERE token = ?2 AND user = ?3 AND previous = ?4",
|
||||
now,
|
||||
refresh_token,
|
||||
user.id,
|
||||
logout_info.token
|
||||
)
|
||||
.execute(&app_state.database)
|
||||
.await?;
|
||||
if result.rows_affected() == 0 {
|
||||
return Ok(HttpResponse::Unauthorized().finish());
|
||||
}
|
||||
let mut refresh_token_cookie = Cookie::new("refresh_token", "");
|
||||
refresh_token_cookie.make_removal();
|
||||
Ok(HttpResponse::Ok().cookie(refresh_token_cookie).finish())
|
||||
}
|
||||
None => Ok(HttpResponse::Unauthorized().finish()),
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_authentication(user: &User, password: &Password) -> Result<bool, Box<dyn Error>> {
|
||||
let argon2 = Argon2::default();
|
||||
let hash = PasswordHash::new(&user.hash)?;
|
||||
|
||||
Reference in New Issue
Block a user