diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-09-18 22:49:38 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-09-20 16:42:44 -0400 |
| commit | 22348bfa35f009e62abe2f30863e0434079a1fe2 (patch) | |
| tree | c5b5b5e660a1ee2a05785f4669102c1023b6e7b0 /src/login | |
| parent | aafdeb9ffaf9a993ca4462b3422667e04469b2e3 (diff) | |
Remove the HTML client, and expose a JSON API.
This API structure fell out of a conversation with Kit. Described loosely:
kit: ok
kit: Here's what I'm picturing in a client
kit: list channels, make-new-channel, zero to one active channels, post-to-active.
kit: login/sign-up, logout
owen: you will likely also want "am I logged in" here
kit: sure, whoami
Diffstat (limited to 'src/login')
| -rw-r--r-- | src/login/routes.rs | 73 |
1 files changed, 49 insertions, 24 deletions
diff --git a/src/login/routes.rs b/src/login/routes.rs index 1ed61ce..3052147 100644 --- a/src/login/routes.rs +++ b/src/login/routes.rs @@ -1,19 +1,35 @@ use axum::{ - extract::{Form, State}, + extract::{Json, State}, http::StatusCode, - response::{IntoResponse, Redirect, Response}, - routing::post, + response::{IntoResponse, Response}, + routing::{get, post}, Router, }; -use crate::{app::App, clock::RequestedAt, error::InternalError}; +use crate::{app::App, clock::RequestedAt, error::InternalError, repo::login::Login}; use super::{app, extract::IdentityToken}; pub fn router() -> Router<App> { Router::new() - .route("/login", post(on_login)) - .route("/logout", post(on_logout)) + .route("/api/boot", get(boot)) + .route("/api/auth/login", post(on_login)) + .route("/api/auth/logout", post(on_logout)) +} + +async fn boot(login: Login) -> Boot { + Boot { login } +} + +#[derive(serde::Serialize)] +struct Boot { + login: Login, +} + +impl IntoResponse for Boot { + fn into_response(self) -> Response { + Json(self).into_response() + } } #[derive(serde::Deserialize)] @@ -26,24 +42,15 @@ async fn on_login( State(app): State<App>, RequestedAt(now): RequestedAt, identity: IdentityToken, - Form(form): Form<LoginRequest>, -) -> Result<LoginSuccess, LoginError> { + Json(request): Json<LoginRequest>, +) -> Result<(IdentityToken, StatusCode), LoginError> { let token = app .logins() - .login(&form.name, &form.password, now) + .login(&request.name, &request.password, now) .await .map_err(LoginError)?; let identity = identity.set(&token); - Ok(LoginSuccess(identity)) -} - -struct LoginSuccess(IdentityToken); - -impl IntoResponse for LoginSuccess { - fn into_response(self) -> Response { - let Self(identity) = self; - (identity, Redirect::to("/")).into_response() - } + Ok((identity, StatusCode::NO_CONTENT)) } struct LoginError(app::LoginError); @@ -55,21 +62,39 @@ impl IntoResponse for LoginError { app::LoginError::Rejected => { (StatusCode::UNAUTHORIZED, "invalid name or password").into_response() } - app::LoginError::DatabaseError(error) => InternalError::from(error).into_response(), - app::LoginError::PasswordHashError(error) => InternalError::from(error).into_response(), + other => InternalError::from(other).into_response(), } } } +#[derive(serde::Deserialize)] +struct LogoutRequest {} + async fn on_logout( State(app): State<App>, identity: IdentityToken, -) -> Result<impl IntoResponse, InternalError> { + // This forces the only valid request to be `{}`, and not the infinite + // variation allowed when there's no body extractor. + Json(LogoutRequest {}): Json<LogoutRequest>, +) -> Result<(IdentityToken, StatusCode), LogoutError> { if let Some(secret) = identity.secret() { - app.logins().logout(secret).await?; + app.logins().logout(secret).await.map_err(LogoutError)?; } let identity = identity.clear(); + Ok((identity, StatusCode::NO_CONTENT)) +} + +struct LogoutError(app::ValidateError); - Ok((identity, Redirect::to("/"))) +impl IntoResponse for LogoutError { + fn into_response(self) -> Response { + let Self(error) = self; + match error { + error @ app::ValidateError::InvalidToken => { + (StatusCode::UNAUTHORIZED, error.to_string()).into_response() + } + other => InternalError::from(other).into_response(), + } + } } |
