about summary refs log tree commit diff
path: root/splorm/src/handler
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2026-09-25 03:56:11 -0500
committergennyble <gen@nyble.dev>2026-09-25 03:56:11 -0500
commitba592de54cd3dce52eb2096dabbe748107f5db2b (patch)
tree4a23377e9788630e09c669e9ed18aecd27673ee7 /splorm/src/handler
parentf4d8e8d03c1b80d5c59ee85631d5a78048892a32 (diff)
splorm auth
Diffstat (limited to 'splorm/src/handler')
-rw-r--r--splorm/src/handler/api/auth.rs87
-rw-r--r--splorm/src/handler/api/mod.rs (renamed from splorm/src/handler/api.rs)10
2 files changed, 95 insertions, 2 deletions
diff --git a/splorm/src/handler/api/auth.rs b/splorm/src/handler/api/auth.rs
new file mode 100644
index 0000000..ff1e990
--- /dev/null
+++ b/splorm/src/handler/api/auth.rs
@@ -0,0 +1,87 @@
+use axum::{body::Body, extract::State, response::Response};
+use gny::web::cookie::{self, SetCookie};
+use sessionhash::RequestContext;
+
+use crate::{Query, RuntimeError, state::SplormState};
+
+/// POST /auth/register
+pub async fn register(
+	State(state): State<SplormState>,
+	ctx: RequestContext,
+	body: String,
+) -> Result<Response, RuntimeError> {
+	tracing::trace!("{ctx} GET /api/register");
+
+	macro_rules! get {
+		($query:ident $field:literal) => {
+			$query
+				.get_first_value($field)
+				.ok_or(RuntimeError::incomplete_form($field))
+		};
+	}
+
+	let query: Query = body.parse()?;
+	let name = get!(query "name")?;
+	let plaintext_password = get!(query "password")?.to_owned();
+
+	let result = knowing::user_register(state.database(), name, plaintext_password).await;
+	let _user = match result {
+		Ok(user) => user,
+		Err(knowing::Error::DatabaseError(dbe)) => return Err(dbe.into()),
+		_ => unreachable!(),
+	};
+
+	tracing::info!("registered user {name}");
+
+	Response::builder()
+		.status(302)
+		.header("Location", "/")
+		.body(Body::from(format!("registered user {name}")))
+		.map_err(|_| RuntimeError::Unknown)
+}
+
+/// POST /auth/login
+pub async fn login(
+	State(state): State<SplormState>,
+	ctx: RequestContext,
+	body: String,
+) -> Result<Response, RuntimeError> {
+	tracing::trace!("{ctx} GET /api/login");
+
+	macro_rules! get {
+		($query:ident $field:literal) => {
+			$query
+				.get_first_value($field)
+				.ok_or(RuntimeError::incomplete_form($field))
+		};
+	}
+
+	let query: Query = body.parse()?;
+	let name = get!(query "name")?;
+	let plaintext_password = get!(query "password")?.to_owned();
+
+	let result = knowing::user_login(state.database(), name, plaintext_password).await;
+	let session = match result {
+		Ok(user) => user,
+		Err(knowing::Error::DatabaseError(dbe)) => return Err(dbe.into()),
+		Err(knowing::Error::AuthenticationFailed) => {
+			return Err(RuntimeError::authentication_failed(name.to_owned()));
+		}
+		_ => unreachable!(),
+	};
+
+	let session_cookie = SetCookie::new("sid", session.sessionid().as_str())
+		.max_age(Some(cookie::WEEK))
+		.secure(true)
+		.httponly(true)
+		.path(Some("/"));
+
+	tracing::info!("user {name} logged in");
+
+	Response::builder()
+		.status(302)
+		.header("Location", "/")
+		.header("Set-Cookie", session_cookie.as_string())
+		.body(Body::from(format!("registered user {name}")))
+		.map_err(|_| RuntimeError::Unknown)
+}
diff --git a/splorm/src/handler/api.rs b/splorm/src/handler/api/mod.rs
index f360b87..a7bfd4b 100644
--- a/splorm/src/handler/api.rs
+++ b/splorm/src/handler/api/mod.rs
@@ -1,16 +1,22 @@
+mod auth;
+
 use axum::{
 	Router,
 	body::Body,
 	extract::{Path, State},
 	response::Response,
-	routing::get,
+	routing::{get, post},
 };
 use sessionhash::RequestContext;
 
 use crate::{RuntimeError, fs, state::SplormState};
 
+/// Routes nested under /api
 pub fn routes() -> Router<SplormState> {
-	Router::new().route("/stats/now/{name}", get(stats))
+	Router::new()
+		.route("/auth/register", post(auth::register))
+		.route("/auth/login", post(auth::login))
+		.route("/stats/now/{name}", get(stats))
 }
 
 async fn stats(