From 3edf10edd9e45dd3d408db673364670eb24cbb59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 27 Apr 2022 21:17:39 +0200 Subject: [PATCH] allow auth token in cookie --- Cargo.lock | 22 +--------------------- Cargo.toml | 4 +++- src/dns/mod.rs | 1 + src/models/user.rs | 37 +++++++++++++++++++++++++++---------- src/routes/users.rs | 17 ++++++++++++++++- 5 files changed, 48 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1743a8e..841a8b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -587,25 +587,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" -[[package]] -name = "h2" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util 0.7.1", - "tracing", -] - [[package]] name = "hashbrown" version = "0.11.2" @@ -695,7 +676,6 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", "http", "http-body", "httparse", @@ -933,6 +913,7 @@ dependencies = [ "rocket_sync_db_pools", "serde", "serde_json", + "time 0.3.9", "tokio", "trust-dns-client", "trust-dns-proto", @@ -1752,7 +1733,6 @@ dependencies = [ "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 83bfd03..6bb22c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ trust-dns-client = { version = "0.21", features = ["dnssec-openssl"] } trust-dns-proto = "0.21" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "6bdd2f8", version = "0.5.0-rc.1", features = ["json"] } +rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "6bdd2f8", version = "0.5.0-rc.1", features = ["json"], default-features = false } rocket_sync_db_pools = { git = "https://github.com/SergioBenitez/Rocket", rev = "6bdd2f8", default-features = false, features = ["diesel_sqlite_pool"], version = "0.1.0-rc.1"} base64 = "0.13.0" uuid = { version = "0.8.2", features = ["v4", "serde"] } @@ -27,3 +27,5 @@ argon2 = {version = "0.4", default-features = false, features = ["alloc", "passw rand = "0.8" # From trust-dns-client futures-util = { version = "0.3.5", default-features = false, features = ["std"] } +# From rocket / cookie-rs +time = "0.3" diff --git a/src/dns/mod.rs b/src/dns/mod.rs index e788503..48c6e8b 100644 --- a/src/dns/mod.rs +++ b/src/dns/mod.rs @@ -23,6 +23,7 @@ use rocket::outcome::try_outcome; use crate::config::Config; + #[rocket::async_trait] impl<'r> FromRequest<'r> for Box { type Error = (); diff --git a/src/models/user.rs b/src/models/user.rs index d47e894..2802f25 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -21,6 +21,7 @@ use crate::models::auth::Session; const BEARER: &str = "Bearer "; const AUTH_HEADER: &str = "Authorization"; +pub const COOKIE_NAME: &str = "session_id"; #[derive(Debug, DbEnum, Deserialize, Clone)] @@ -114,21 +115,37 @@ impl UserInfo { } } +fn get_token_from_header<'r>(request: &'r Request<'_>) -> Outcome { + let auth_header = match request.headers().get_one(AUTH_HEADER) { + None => return Outcome::Forward(()), + Some(auth_header) => auth_header, + }; + + let token = if auth_header.starts_with(BEARER) { + auth_header.trim_start_matches(BEARER).to_string() + } else { + return ErrorResponse::from(UserError::MalformedHeader).into(); + }; + + Outcome::Success(token) +} + +fn get_token_from_cookie<'r>(request: &'r Request<'_>) -> Outcome { + match request.cookies().get(COOKIE_NAME) { + None => Outcome::Forward(()), + Some(session_cookie) => Outcome::Success(session_cookie.value().to_string()), + } +} + #[rocket::async_trait] impl<'r> FromRequest<'r> for UserInfo { type Error = ErrorResponse; async fn from_request(request: &'r Request<'_>) -> Outcome { - let auth_header = match request.headers().get_one(AUTH_HEADER) { - None => return Outcome::Forward(()), - Some(auth_header) => auth_header, - }; - - let token = if auth_header.starts_with(BEARER) { - auth_header.trim_start_matches(BEARER).to_string() - } else { - return ErrorResponse::from(UserError::MalformedHeader).into() - }; + let token = try_outcome!( + get_token_from_header(request) + .forward_then(|_| get_token_from_cookie(request)) + ); let conn = try_outcome!(request.guard::().await.map_failure(make_500)); diff --git a/src/routes/users.rs b/src/routes/users.rs index c770d23..4b97be2 100644 --- a/src/routes/users.rs +++ b/src/routes/users.rs @@ -1,3 +1,4 @@ +use rocket::http::{Cookie, SameSite, CookieJar}; use rocket::serde::json::Json; use rocket::State; use rocket::http::Status; @@ -5,13 +6,15 @@ use rocket::http::Status; use crate::config::Config; use crate::DbConn; use crate::models; +use time; #[post("/users/me/token", data = "")] pub async fn create_auth_token( conn: DbConn, config: &State, - auth_request: Json + auth_request: Json, + cookies: &CookieJar<'_> ) -> Result, models::ErrorResponse> { let session_duration = config.web_app.token_duration; @@ -26,6 +29,18 @@ pub async fn create_auth_token( models::Session::new(c, &user_info, session_duration) }).await?; + // Conversion between different date / time libraries, very cursed, I don't like that + // About unwrap: I guess too bad if session time is over year 9999 (current max time if time-rs) + let expires = time::OffsetDateTime::from_unix_timestamp(session.expires_at.timestamp()).unwrap(); + + let session_cookie = Cookie::build(models::user::COOKIE_NAME, session.session_id.clone()) + .same_site(SameSite::Strict) + .secure(true) + .http_only(true) + .expires(expires) + .finish(); + + cookies.add(session_cookie); Ok(Json(session)) }