diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-09-12 00:15:32 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-09-12 00:27:24 -0400 |
| commit | 74ef9102a62f713f36f6f8412732be9d837d8d2c (patch) | |
| tree | 9f0ea3137001d2e171af53aec370c57ed7fb02a9 /src/login/routes.rs | |
| parent | f2f820370efbd5c6d0f304f781284a9f68990e21 (diff) | |
Push most endpoint and extractor logic into functoins of `App`.
This is, again, groundwork for logic that requires more than just a database connection.
The login process has been changed to be more conventional, attempting login _before_ account creation rather than after it. This was not previously possible, because the data access methods used to perform these steps did not return enough information to carry out the workflow in that order. Separating storage from password validation and hashing forces the issue, and makes it clearer _at the App_ whether an account exists or not.
This does introduce the possibility of two racing inserts trying to lay claim to the same username. Transaction isolation should ensure that only one of them "wins," which is what you get before this change anyways.
Diffstat (limited to 'src/login/routes.rs')
| -rw-r--r-- | src/login/routes.rs | 38 |
1 files changed, 3 insertions, 35 deletions
diff --git a/src/login/routes.rs b/src/login/routes.rs index 9cefe38..816926e 100644 --- a/src/login/routes.rs +++ b/src/login/routes.rs @@ -8,10 +8,7 @@ use axum::{ use crate::{app::App, clock::RequestedAt, error::InternalError}; -use super::{ - extract::IdentityToken, - repo::{logins::Provider as _, tokens::Provider as _}, -}; +use super::extract::IdentityToken; pub fn router() -> Router<App> { Router::new() @@ -31,34 +28,7 @@ async fn on_login( identity: IdentityToken, Form(form): Form<LoginRequest>, ) -> Result<impl IntoResponse, InternalError> { - let mut tx = app.db.begin().await?; - - // Spelling the following in the more conventional form, - // if let Some(…) = create().await? {} - // else if let Some(…) = validate().await? {} - // else {} - // pushes the specifics of whether the returned error types are Send or not - // (they aren't) into the type of this function's generated Futures, which - // in turn makes this function unusable as an Axum handler. - let login = tx.logins().create(&form.name, &form.password).await?; - let login = if login.is_some() { - login - } else { - tx.logins().authenticate(&form.name, &form.password).await? - }; - - // If `login` is Some, then we have an identity and can issue an identity - // token. If `login` is None, then neither creating a new login nor authenticating - // an existing one succeeded, and we must reject the attempt. - // - // These properties will be transferred to `token`, as well. - let token = if let Some(login) = login { - Some(tx.tokens().issue(&login.id, now).await?) - } else { - None - }; - - tx.commit().await?; + let token = app.logins().login(&form.name, &form.password, now).await?; let resp = if let Some(token) = token { let identity = identity.set(&token); @@ -91,9 +61,7 @@ async fn on_logout( identity: IdentityToken, ) -> Result<impl IntoResponse, InternalError> { if let Some(secret) = identity.secret() { - let mut tx = app.db.begin().await?; - tx.tokens().revoke(secret).await?; - tx.commit().await?; + app.logins().logout(secret).await?; } let identity = identity.clear(); |
