summaryrefslogtreecommitdiff
path: root/src/login
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-09-18 22:49:38 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-09-20 16:42:44 -0400
commit22348bfa35f009e62abe2f30863e0434079a1fe2 (patch)
treec5b5b5e660a1ee2a05785f4669102c1023b6e7b0 /src/login
parentaafdeb9ffaf9a993ca4462b3422667e04469b2e3 (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.rs73
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(),
+ }
+ }
}