1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
use crate::web_client::utils::parse_cookies;
use axum::body::Body;
use axum::http::header::SET_COOKIE;
use axum::{extract::Request, http::StatusCode, middleware::Next, response::Response};
use axum_extra::extract::cookie::{Cookie, SameSite};
use zellij_utils::web_authentication_tokens::{
hash_token, is_session_token_read_only, validate_session_token,
};
#[derive(Clone)]
pub struct SessionTokenHash(pub String);
#[derive(Clone, Copy)]
pub struct IsReadOnly(pub bool);
pub async fn auth_middleware(request: Request, next: Next) -> Result<Response, StatusCode> {
let cookies = parse_cookies(&request);
let session_token = match cookies.get("session_token") {
Some(token) => token.clone(),
None => return Err(StatusCode::UNAUTHORIZED),
};
match validate_session_token(&session_token) {
Ok(true) => {
// Check if this is a read-only token
let is_read_only = is_session_token_read_only(&session_token).unwrap_or(true);
// Compute session token hash for client ownership verification
let session_token_hash = hash_token(&session_token);
// Store in request extensions for downstream handlers
let mut request = request;
request.extensions_mut().insert(IsReadOnly(is_read_only));
request
.extensions_mut()
.insert(SessionTokenHash(session_token_hash));
let response = next.run(request).await;
Ok(response)
},
Ok(false) | Err(_) => {
// revoke session_token as if it exists it's no longer valid
let mut response = Response::builder()
.status(StatusCode::UNAUTHORIZED)
.body(Body::empty())
.unwrap();
// Clear both secure and non-secure versions
// in case the user was on http before and is now on https
// or vice versa
let clear_cookies = [
Cookie::build(("session_token", ""))
.http_only(true)
.secure(false)
.same_site(SameSite::Strict)
.path("/")
.max_age(time::Duration::seconds(0))
.build(),
Cookie::build(("session_token", ""))
.http_only(true)
.secure(true)
.same_site(SameSite::Strict)
.path("/")
.max_age(time::Duration::seconds(0))
.build(),
];
for cookie in clear_cookies {
response
.headers_mut()
.append(SET_COOKIE, cookie.to_string().parse().unwrap());
}
Ok(response)
},
}
}
|