diff options
| author | tv <tv@krebsco.de> | 2026-11-07 22:35:22 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-07 22:35:22 +0100 |
| commit | a99bacc0c90ca7a7f879cec8a8fb33f4aa14e84f (patch) | |
| tree | 2de7dfad5ac866df8e90560a06e93a47a8f7f173 /src/api/login.rs | |
| parent | 91d1bb60cb7a8d92e4e1966dabbcb50a2406c75e (diff) | |
auth: the session cookie lets in reads only
The cookie lets in GET, HEAD and the session's own requests; anything
changing something brings the token itself, which no page of another
site or port can make a browser send. The dashboard's writes send it.
Auth plan M3 (browsers).
Diffstat (limited to 'src/api/login.rs')
| -rw-r--r-- | src/api/login.rs | 15 |
1 files changed, 13 insertions, 2 deletions
diff --git a/src/api/login.rs b/src/api/login.rs index de6e390..1907bac 100644 --- a/src/api/login.rs +++ b/src/api/login.rs @@ -54,6 +54,16 @@ pub(crate) struct Token { /// `HttpOnly`, `SameSite=Strict`, so no script and no other site's page sends it. pub(crate) const SESSION_COOKIE: &str = "continuum-token"; +/// Whether the session cookie lets `request` in: one that reads (`GET`, `HEAD`), or +/// renews or ends the session. One that changes anything brings the token itself +/// (`Authorization`), as a page of another site, or of another port of the same host +/// (which a cookie doesn't tell apart), can't make a browser send it. +fn cookie_lets_in(request: &Request<Body>) -> bool { + use axum::http::Method; + let method = request.method(); + method == Method::GET || method == Method::HEAD || request.uri().path() == "/auth/session" +} + /// The session cookie's token, if the request brings one. fn session_cookie(request: &Request<Body>) -> Option<String> { request @@ -70,7 +80,8 @@ fn session_cookie(request: &Request<Body>) -> Option<String> { /// A request with `Authorization: Bearer <token>`: its user is the token's, if this /// node takes it (`token::verify`); `401`, saying why, if not. One without /// `Authorization` but with the session cookie: the cookie's token's, if taken (if not, -/// as if it brought none). Others go on to `auth::basic_auth`. +/// as if it brought none), for reading only (`cookie_lets_in`). Others go on to +/// `auth::basic_auth`. pub(crate) async fn bearer( State(state): State<AppState>, mut request: Request<Body>, @@ -86,7 +97,7 @@ pub(crate) async fn bearer( Some(text) => (text.to_owned(), false), None => return next.run(request).await, }, - None => match session_cookie(&request) { + None => match session_cookie(&request).filter(|_| cookie_lets_in(&request)) { Some(text) => (text, true), None => return next.run(request).await, }, |
