allow auth token in cookie

main
Hannaeko 2022-04-27 21:17:39 +02:00
parent a83a099f34
commit 3edf10edd9
5 changed files with 48 additions and 33 deletions

22
Cargo.lock generated
View File

@ -587,25 +587,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" 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]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.11.2" version = "0.11.2"
@ -695,7 +676,6 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2",
"http", "http",
"http-body", "http-body",
"httparse", "httparse",
@ -933,6 +913,7 @@ dependencies = [
"rocket_sync_db_pools", "rocket_sync_db_pools",
"serde", "serde",
"serde_json", "serde_json",
"time 0.3.9",
"tokio", "tokio",
"trust-dns-client", "trust-dns-client",
"trust-dns-proto", "trust-dns-proto",
@ -1752,7 +1733,6 @@ dependencies = [
"futures-sink", "futures-sink",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
"tracing",
] ]
[[package]] [[package]]

View File

@ -11,7 +11,7 @@ trust-dns-client = { version = "0.21", features = ["dnssec-openssl"] }
trust-dns-proto = "0.21" trust-dns-proto = "0.21"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" 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"} 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" base64 = "0.13.0"
uuid = { version = "0.8.2", features = ["v4", "serde"] } 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" rand = "0.8"
# From trust-dns-client # From trust-dns-client
futures-util = { version = "0.3.5", default-features = false, features = ["std"] } futures-util = { version = "0.3.5", default-features = false, features = ["std"] }
# From rocket / cookie-rs
time = "0.3"

View File

@ -23,6 +23,7 @@ use rocket::outcome::try_outcome;
use crate::config::Config; use crate::config::Config;
#[rocket::async_trait] #[rocket::async_trait]
impl<'r> FromRequest<'r> for Box<dyn RecordConnector> { impl<'r> FromRequest<'r> for Box<dyn RecordConnector> {
type Error = (); type Error = ();

View File

@ -21,6 +21,7 @@ use crate::models::auth::Session;
const BEARER: &str = "Bearer "; const BEARER: &str = "Bearer ";
const AUTH_HEADER: &str = "Authorization"; const AUTH_HEADER: &str = "Authorization";
pub const COOKIE_NAME: &str = "session_id";
#[derive(Debug, DbEnum, Deserialize, Clone)] #[derive(Debug, DbEnum, Deserialize, Clone)]
@ -114,11 +115,7 @@ impl UserInfo {
} }
} }
#[rocket::async_trait] fn get_token_from_header<'r>(request: &'r Request<'_>) -> Outcome<String, ErrorResponse> {
impl<'r> FromRequest<'r> for UserInfo {
type Error = ErrorResponse;
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let auth_header = match request.headers().get_one(AUTH_HEADER) { let auth_header = match request.headers().get_one(AUTH_HEADER) {
None => return Outcome::Forward(()), None => return Outcome::Forward(()),
Some(auth_header) => auth_header, Some(auth_header) => auth_header,
@ -127,9 +124,29 @@ impl<'r> FromRequest<'r> for UserInfo {
let token = if auth_header.starts_with(BEARER) { let token = if auth_header.starts_with(BEARER) {
auth_header.trim_start_matches(BEARER).to_string() auth_header.trim_start_matches(BEARER).to_string()
} else { } else {
return ErrorResponse::from(UserError::MalformedHeader).into() return ErrorResponse::from(UserError::MalformedHeader).into();
}; };
Outcome::Success(token)
}
fn get_token_from_cookie<'r>(request: &'r Request<'_>) -> Outcome<String, ErrorResponse> {
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<Self, Self::Error> {
let token = try_outcome!(
get_token_from_header(request)
.forward_then(|_| get_token_from_cookie(request))
);
let conn = try_outcome!(request.guard::<DbConn>().await.map_failure(make_500)); let conn = try_outcome!(request.guard::<DbConn>().await.map_failure(make_500));
let session_res = conn.run(move |c| { let session_res = conn.run(move |c| {

View File

@ -1,3 +1,4 @@
use rocket::http::{Cookie, SameSite, CookieJar};
use rocket::serde::json::Json; use rocket::serde::json::Json;
use rocket::State; use rocket::State;
use rocket::http::Status; use rocket::http::Status;
@ -5,13 +6,15 @@ use rocket::http::Status;
use crate::config::Config; use crate::config::Config;
use crate::DbConn; use crate::DbConn;
use crate::models; use crate::models;
use time;
#[post("/users/me/token", data = "<auth_request>")] #[post("/users/me/token", data = "<auth_request>")]
pub async fn create_auth_token( pub async fn create_auth_token(
conn: DbConn, conn: DbConn,
config: &State<Config>, config: &State<Config>,
auth_request: Json<models::AuthTokenRequest> auth_request: Json<models::AuthTokenRequest>,
cookies: &CookieJar<'_>
) -> Result<Json<models::Session>, models::ErrorResponse> { ) -> Result<Json<models::Session>, models::ErrorResponse> {
let session_duration = config.web_app.token_duration; 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) models::Session::new(c, &user_info, session_duration)
}).await?; }).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)) Ok(Json(session))
} }