Add authentication
Proving who somebody is and starting a session for them are separate operations. login_with_caldav does the first by asking the CalDAV server whether the credentials work; begin_session does the second and knows nothing about how the question was answered. OIDC arrives as a second way to reach begin_session, not as a second scheme threaded through everything -- which is what v1 had, with a JWT for most of the app and a separate SQLite session_token used only by the preferences API. The token lives in an HttpOnly cookie and nowhere else. v1 kept a JWT and the CalDAV password in localStorage, readable by any script on the origin, and re-sent the password in a header on every request. Here the password never leaves the server: it is encrypted with XChaCha20-Poly1305 and caldav_for is the only path back, handing out a client rather than a credential. Failed decryption is an error, not a subtly wrong password -- the AEAD tag is checked, so a tampered row surfaces here instead of as a mysterious CalDAV rejection later. A wrong password and an unreachable server stay distinct, because telling somebody their password is wrong when the server is down sends them to reset one that was fine. Errors carry a stable code alongside their message, so a client can branch on them. Internal ones say nothing about the inside of the server; the detail goes to the log. Tests go through router(), the same function main calls -- v1's suite rebuilt the route table and tested a copy until it stopped compiling. Skipping is now loud: a skipped test reports "ok", so run.sh sets RUNWAY_REQUIRE_CALDAV=1 and not running becomes a failure.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
//! The router.
|
||||
//!
|
||||
//! One function, used by both `main` and the tests. v1's integration suite
|
||||
//! rebuilt the route table itself, which meant it tested a copy — and when the
|
||||
//! real one changed, the copy silently kept passing until it stopped compiling
|
||||
//! altogether.
|
||||
|
||||
use crate::routes;
|
||||
use crate::state::AppState;
|
||||
use axum::Router;
|
||||
use axum::routing::{get, post};
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
pub fn router(state: AppState) -> Router {
|
||||
Router::new()
|
||||
.route("/api/health", get(routes::health))
|
||||
.route("/api/auth/login", post(routes::auth::login))
|
||||
.route("/api/auth/logout", post(routes::auth::logout))
|
||||
.route("/api/auth/session", get(routes::auth::current_session))
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.with_state(state)
|
||||
}
|
||||
Reference in New Issue
Block a user