Finish sign out

This commit is contained in:
eraden 2024-01-27 22:45:39 +01:00
parent 93155b6cbf
commit 25390c0c56
5 changed files with 91 additions and 35 deletions

36
Cargo.lock generated
View File

@ -86,9 +86,9 @@ dependencies = [
[[package]] [[package]]
name = "actix-jwt-session" name = "actix-jwt-session"
version = "1.0.4" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6317d3303618eea36d68898bf720ce94d8b5bb2bf1a23c8ffdf714024f0a49a6" checksum = "5417207a8cf4ac0c420464ea0b457143a4a172a0ee39eb5adf6cb70fa21c3065"
dependencies = [ dependencies = [
"actix-web", "actix-web",
"argon2", "argon2",
@ -2047,9 +2047,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]] [[package]]
name = "libnghttp2-sys" name = "libnghttp2-sys"
version = "0.1.8+1.55.1" version = "0.1.9+1.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fae956c192dadcdb5dace96db71fa0b827333cce7c7b38dc71446f024d8a340" checksum = "b57e858af2798e167e709b9d969325b6d8e9d50232fcbc494d7d54f976854a64"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -2079,9 +2079,9 @@ dependencies = [
[[package]] [[package]]
name = "libz-sys" name = "libz-sys"
version = "1.1.14" version = "1.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "295c17e837573c8c821dbaeb3cceb3d745ad082f7572191409e69cbc1b3fd050" checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -2714,18 +2714,18 @@ dependencies = [
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.3" version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0"
dependencies = [ dependencies = [
"pin-project-internal", "pin-project-internal",
] ]
[[package]] [[package]]
name = "pin-project-internal" name = "pin-project-internal"
version = "1.1.3" version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3680,9 +3680,9 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.195" version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
@ -3741,9 +3741,9 @@ checksum = "794e44574226fc701e3be5c651feb7939038fc67fb73f6f4dd5c4ba90fd3be70"
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.195" version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3752,9 +3752,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.111" version = "1.0.112"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" checksum = "4d1bd37ce2324cf3bf85e5a25f96eb4baf0d5aa6eba43e7ae8958870c4ec48ed"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -5144,9 +5144,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.5.34" version = "0.5.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" checksum = "1931d78a9c73861da0134f453bb1f790ce49b2e30eba8410b4b79bac72b46a2d"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]

View File

@ -4,12 +4,11 @@ use actix_jwt_session::{
Duration, Hashing, JwtTtl, RefreshTtl, SessionStorage, JWT_HEADER_NAME, REFRESH_HEADER_NAME, Duration, Hashing, JwtTtl, RefreshTtl, SessionStorage, JWT_HEADER_NAME, REFRESH_HEADER_NAME,
}; };
use actix_web::web::{Data, ServiceConfig}; use actix_web::web::{Data, ServiceConfig};
use actix_web::{delete, get, HttpRequest, HttpResponse}; use actix_web::{HttpRequest, HttpResponse};
use entities::prelude::Users; use entities::prelude::Users;
use entities::users::Model as User; use entities::users::Model as User;
use reqwest::StatusCode; use reqwest::StatusCode;
use sea_orm::prelude::*; use sea_orm::prelude::*;
use sea_orm::DatabaseConnection;
use sea_orm::*; use sea_orm::*;
use uuid::Uuid; use uuid::Uuid;
@ -28,9 +27,9 @@ pub struct AuthResponseBody {
pub fn configure(http_client: reqwest::Client, config: &mut ServiceConfig) { pub fn configure(http_client: reqwest::Client, config: &mut ServiceConfig) {
config config
.service(email_check::email_check) .service(email_check::email_check)
.service(oauth)
.service(sign_in::sign_in) .service(sign_in::sign_in)
.service(sign_up::sign_up); .service(sign_up::sign_up)
.service(sign_out::sign_out);
social_auth::configure(http_client, config); social_auth::configure(http_client, config);
} }

View File

@ -1,14 +1,52 @@
use actix_jwt_session::{Authenticated, SessionStorage}; use actix_jwt_session::{Authenticated, RefreshToken, SessionStorage};
use actix_web::HttpRequest;
use actix_web::{post, web::Data, HttpResponse}; use actix_web::{post, web::Data, HttpResponse};
use sea_orm::DatabaseConnection; use entities::prelude::Users;
use entities::users::{ActiveModel as UserModel, Column};
use sea_orm::prelude::*;
use sea_orm::Set;
use serde_json::json;
use crate::models::{Error, JsonError};
use crate::session::AppClaims; use crate::session::AppClaims;
use crate::utils::extract_req_ip;
#[post("/sign-out")] #[post("/sign-out")]
pub async fn sign_out( pub async fn sign_out(
_db: Data<DatabaseConnection>, req: HttpRequest,
claims: Authenticated<AppClaims>, db: Data<DatabaseConnection>,
claims: Authenticated<RefreshToken>,
session: Data<SessionStorage>, session: Data<SessionStorage>,
) -> HttpResponse { ) -> Result<HttpResponse, JsonError> {
HttpResponse::NotImplemented().finish() let Ok(access_token) = session.find_jwt::<AppClaims>(claims.access_jti()).await else {
return Err(JsonError::new("No refresh token provided"));
};
let mut user: UserModel = match Users::find()
.filter(Column::Id.eq(access_token.account_id()))
.one(&**db)
.await
{
Ok(Some(user)) => user,
Ok(None) => {
return Err(JsonError::new(
"User for this token does not exists. Please contact administrator",
))
}
Err(e) => {
tracing::error!("Failed to connect to database on sign-out: {e}");
return Err(JsonError::new(Error::DatabaseError));
}
}
.into();
user.last_logout_ip = Set(extract_req_ip(&req)?);
user.last_logout_time = Set(Some(chrono::Utc::now().fixed_offset()));
user.save(&**db).await.map_err(|e| {
tracing::error!("Failed to update user in sign-out: {e}");
return JsonError::new(Error::DatabaseError);
})?;
session.erase::<AppClaims>(access_token.jwt_id).await.ok();
Ok(HttpResponse::Ok().json(json!({ "message": "success" } )))
} }

View File

@ -22,7 +22,7 @@ async fn main() {
dotenv::from_filename(".env.development").ok(); dotenv::from_filename(".env.development").ok();
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
if let Ok(entry_dsn) = env::var("SENTRY_DSN") { if let Ok(sentry_dsn) = env::var("SENTRY_DSN") {
let _guard = sentry::init(sentry_dsn); let _guard = sentry::init(sentry_dsn);
} }
@ -54,6 +54,8 @@ async fn main() {
.with_refresh_header(REFRESH_HEADER_NAME) .with_refresh_header(REFRESH_HEADER_NAME)
// Check if cookie JWT exists and contains encoded JWT // Check if cookie JWT exists and contains encoded JWT
.with_refresh_cookie(REFRESH_COOKIE_NAME) .with_refresh_cookie(REFRESH_COOKIE_NAME)
.with_jwt_json(&["refresh_token"])
.with_jwt_json(&["access_token"])
.finish(); .finish();
let jwt_ttl = JwtTtl(Duration::days(9999)); let jwt_ttl = JwtTtl(Duration::days(9999));
let refresh_ttl = RefreshTtl(Duration::days(3 * 31 * 999)); let refresh_ttl = RefreshTtl(Duration::days(3 * 31 * 999));

View File

@ -23,17 +23,34 @@ use uuid::Uuid;
use crate::http::OAuthError; use crate::http::OAuthError;
use crate::{http::AuthError, models::Error}; use crate::{http::AuthError, models::Error};
pub fn extract_req_info(req: &HttpRequest) -> Result<(String, String, String), Error> { pub fn extract_req_ip(req: &HttpRequest) -> Result<String, Error> {
let ip = req.peer_addr().ok_or(AuthError::NoPeerAddr)?.ip(); Ok(req
let user_agent = req .peer_addr()
.ok_or(AuthError::NoPeerAddr)?
.ip()
.to_string())
}
pub fn extract_req_uagent(req: &HttpRequest) -> Result<String, Error> {
Ok(req
.headers() .headers()
.get(USER_AGENT) .get(USER_AGENT)
.ok_or(AuthError::NoUserAgent)? .ok_or(AuthError::NoUserAgent)?
.to_str() .to_str()
.map_err(|_| AuthError::InvalidUserAgent)? .map_err(|_| AuthError::InvalidUserAgent)?
.to_string(); .to_string())
let current_site = req.uri().host().ok_or(Error::NoHost)?.to_owned(); }
Ok((ip.to_string(), user_agent, current_site))
pub fn extract_req_current_site(req: &HttpRequest) -> Result<String, Error> {
Ok(req.uri().host().ok_or(Error::NoHost)?.to_owned())
}
pub fn extract_req_info(req: &HttpRequest) -> Result<(String, String, String), Error> {
Ok((
extract_req_ip(&*req)?,
extract_req_uagent(&*req)?,
extract_req_current_site(req)?,
))
} }
pub async fn invites_to_membership( pub async fn invites_to_membership(