From 4eea9d6ab134bdd05506dc85145e72ab186bf2ad Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Mon, 15 Apr 2024 19:30:02 -0500 Subject: feat(api): add logout endpoint to unset token --- src/api/users.rs | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) (limited to 'src/api/users.rs') diff --git a/src/api/users.rs b/src/api/users.rs index 6ed79f2..3ac72d9 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -7,6 +7,7 @@ use axum::{ Json, Router, }; use axum_extra::{ + extract::{cookie::Cookie, CookieJar}, headers::{authorization::Basic, Authorization}, routing::Resource, typed_header::TypedHeaderRejection, @@ -25,9 +26,12 @@ use crate::{ use super::error::Error; pub fn router() -> Router { + let users = Resource::named("users").create(create).show(show); + axum::Router::new() .route("/users/login", get(login)) - .merge(Resource::named("users").create(create).show(show)) + .route("/users/logout", get(logout)) + .merge(users) } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, FromRow)] @@ -71,6 +75,10 @@ pub async fn login( .map_err(Into::into) } +pub async fn logout(claims: AccessClaims, jar: CookieJar) -> Result { + Ok(jar.remove(Cookie::try_from(claims)?)) +} + pub async fn create( State(state): State, Json(RegisterSchema { @@ -141,7 +149,7 @@ mod tests { Router, }; - use axum_extra::headers::authorization::Credentials; + use axum_extra::headers::{authorization::Credentials, Header, HeaderMapExt, SetCookie}; use http_body_util::BodyExt; use sqlx::PgPool; use tower::ServiceExt; @@ -375,4 +383,35 @@ mod tests { Ok(()) } + + #[sqlx::test(fixtures(path = "../../fixtures", scripts("users")))] + async fn test_logout_ok(pool: PgPool) -> TestResult { + setup_test_env(); + + let router = Router::new().merge(router()).with_state(AppState { pool }); + + let request = Request::builder() + .uri("/users/logout") + .method("GET") + .header(COOKIE, HeaderValue::try_from(AccessClaims::new(USER_ID))?) + .body(Body::empty())?; + + let response = router.oneshot(request).await?; + + assert_eq!(StatusCode::OK, response.status()); + + if let Some(set_cookie) = response.headers().typed_get::() { + let mut values = Vec::new(); + set_cookie.encode(&mut values); + for value in values { + let cookie: Cookie = value.to_str()?.parse().unwrap(); + if cookie.name() == "token" { + assert_eq!(cookie.value(), ""); + assert_eq!(cookie.max_age(), Some(time::Duration::ZERO)); + } + } + } + + Ok(()) + } } -- cgit v1.2.3-70-g09d2