Improve security
This commit is contained in:
@@ -1,25 +1,62 @@
|
||||
use actix_web::HttpResponse;
|
||||
use diesel::prelude::*;
|
||||
|
||||
// JWT auth is stateless and there is no token blacklist, so logging out is
|
||||
// purely a client-side action (discarding the stored token). This endpoint
|
||||
// exists so the frontend has something to call and gets a clean response.
|
||||
pub async fn logout() -> HttpResponse {
|
||||
HttpResponse::Ok().finish()
|
||||
use crate::auth::extractor::AuthUser;
|
||||
use crate::database::establish_connection;
|
||||
use crate::error::AppError;
|
||||
use crate::schema::users;
|
||||
|
||||
/// Invalidates every token previously issued for this user by bumping
|
||||
/// `token_version` — the auth middleware rejects tokens whose `tv` claim no
|
||||
/// longer matches the stored value.
|
||||
pub async fn logout(auth_user: AuthUser) -> Result<HttpResponse, AppError> {
|
||||
let mut connection = establish_connection();
|
||||
|
||||
diesel::update(users::table.find(auth_user.0))
|
||||
.set(users::token_version.eq(users::token_version + 1))
|
||||
.execute(&mut connection)?;
|
||||
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use actix_service::Service;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{test, web, App};
|
||||
use actix_web::{test, web, App, HttpMessage};
|
||||
use diesel::prelude::*;
|
||||
|
||||
use super::logout;
|
||||
use crate::auth::extractor::AuthUser;
|
||||
use crate::database::establish_connection;
|
||||
use crate::models::user::rss_user::User;
|
||||
use crate::schema::users;
|
||||
use crate::test_helpers::{delete_user, insert_user};
|
||||
|
||||
#[actix_web::test]
|
||||
async fn logout_returns_ok() {
|
||||
let app = test::init_service(App::new().route("/logout", web::post().to(logout))).await;
|
||||
async fn logout_bumps_token_version() {
|
||||
let mut connection = establish_connection();
|
||||
let user = insert_user(&mut connection, "secret");
|
||||
let user_id = user.id;
|
||||
let initial_version = user.token_version;
|
||||
|
||||
let app = test::init_service(
|
||||
App::new()
|
||||
.wrap_fn(move |req, srv| {
|
||||
req.extensions_mut().insert(AuthUser(user_id));
|
||||
srv.call(req)
|
||||
})
|
||||
.route("/logout", web::post().to(logout)),
|
||||
)
|
||||
.await;
|
||||
let req = test::TestRequest::post().uri("/logout").to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
|
||||
assert_eq!(StatusCode::OK, resp.status());
|
||||
|
||||
let updated: User = users::table.find(user_id).first(&mut connection).unwrap();
|
||||
assert_eq!(initial_version + 1, updated.token_version);
|
||||
|
||||
delete_user(&mut connection, user_id);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user