diff options
Diffstat (limited to 'src/api')
-rw-r--r-- | src/api/account.rs | 64 | ||||
-rw-r--r-- | src/api/users.rs | 19 |
2 files changed, 51 insertions, 32 deletions
diff --git a/src/api/account.rs b/src/api/account.rs index d6a94b5..0087df7 100644 --- a/src/api/account.rs +++ b/src/api/account.rs @@ -1,13 +1,19 @@ -use axum::{extract::State, routing::get, Router}; +use axum::{ + async_trait, + extract::{FromRequestParts, State}, + http::request::Parts, + routing::get, + RequestPartsExt, Router, +}; use axum_extra::{ + either::Either, extract::{cookie::Cookie, CookieJar}, headers::{authorization::Basic, Authorization}, - typed_header::TypedHeaderRejection, TypedHeader, }; use crate::{ - auth::{AccessClaims, RefreshClaims}, + auth::{AccessClaims, Account, RefreshClaims}, state::AppState, }; @@ -21,32 +27,44 @@ pub fn router() -> Router<AppState> { pub async fn login( State(state): State<AppState>, - auth: Result<TypedHeader<Authorization<Basic>>, TypedHeaderRejection>, - claims: Option<RefreshClaims>, -) -> Result<(AccessClaims, RefreshClaims), Error> { - if let Some(refresh_claims) = claims { - return Ok((refresh_claims.refresh(), refresh_claims)); + auth: Either<RefreshClaims, Login>, +) -> Result<(AccessClaims, RefreshClaims), crate::auth::error::Error> { + match auth { + Either::E1(token) => Ok((token.refresh(), token)), + Either::E2(Login(account)) => crate::auth::issue(State(state.clone()), account).await, } - - let TypedHeader(Authorization(basic)) = auth?; - - let user_id = sqlx::query_scalar!("SELECT id FROM user_ WHERE email = $1", basic.username()) - .fetch_optional(&state.pool) - .await? - .ok_or(Error::UserNotFound)?; - - crate::auth::issue( - State(state.clone()), - TypedHeader(Authorization::basic(&user_id.to_string(), basic.password())), - ) - .await - .map_err(Into::into) } pub async fn logout(claims: AccessClaims, jar: CookieJar) -> Result<CookieJar, Error> { Ok(jar.remove(Cookie::try_from(claims)?)) } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Login(Account); + +#[async_trait] +impl FromRequestParts<AppState> for Login { + type Rejection = Error; + + async fn from_request_parts( + parts: &mut Parts, + state: &AppState, + ) -> Result<Self, Self::Rejection> { + let TypedHeader(Authorization(basic)) = + parts.extract::<TypedHeader<Authorization<Basic>>>().await?; + + sqlx::query_scalar!("SELECT id FROM user_ WHERE email = $1", basic.username()) + .fetch_optional(&state.pool) + .await? + .ok_or(Error::UserNotFound) + .map(|id| Account { + id, + password: basic.password().to_string(), + }) + .map(Self) + } +} + #[cfg(test)] mod tests { use super::*; @@ -60,7 +78,7 @@ mod tests { Router, }; - use axum_extra::headers::authorization::Credentials; + use axum_extra::headers::{authorization::Credentials, Authorization}; use http_body_util::BodyExt; use sqlx::PgPool; use tower::ServiceExt; diff --git a/src/api/users.rs b/src/api/users.rs index c8a390d..6ac0bb8 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -11,7 +11,10 @@ use sqlx::FromRow; use time::OffsetDateTime; use uuid::Uuid; -use crate::{auth::AccessClaims, state::AppState}; +use crate::{ + auth::{credentials::Credential, AccessClaims}, + state::AppState, +}; use super::error::Error; @@ -30,7 +33,7 @@ pub struct User { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct RegisterSchema { +pub struct Registration { pub name: String, pub email: String, pub password: String, @@ -38,11 +41,11 @@ pub struct RegisterSchema { pub async fn create( State(state): State<AppState>, - Json(RegisterSchema { + Json(Registration { name, email, password, - }): Json<RegisterSchema>, + }): Json<Registration>, ) -> impl IntoResponse { email_address::EmailAddress::from_str(&email)?; @@ -58,11 +61,9 @@ pub async fn create( } // TODO: Move this into a micro service, possibly behind a feature flag. - let (status, (access, refresh)) = crate::auth::credentials::create( - State(state.clone()), - Json(crate::auth::credentials::Credential { password }), - ) - .await?; + let (status, (access, refresh)) = + crate::auth::credentials::create(State(state.clone()), Json(Credential { password })) + .await?; let user = sqlx::query_as!( User, |