diff options
Diffstat (limited to 'src/auth/credentials.rs')
-rw-r--r-- | src/auth/credentials.rs | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/src/auth/credentials.rs b/src/auth/credentials.rs new file mode 100644 index 0000000..7f92048 --- /dev/null +++ b/src/auth/credentials.rs @@ -0,0 +1,66 @@ +use argon2::{ + password_hash::{rand_core::OsRng, SaltString}, + Argon2, PasswordHasher, +}; +use axum::{ + extract::{Path, State}, + http::StatusCode, +}; +use axum_extra::{ + headers::{authorization::Basic, Authorization}, + routing::Resource, + TypedHeader, +}; +use uuid::Uuid; + +use crate::state::AppState; + +use super::{error::Error, AccessClaims, RefreshClaims}; + +pub fn router() -> Resource<AppState> { + Resource::named("credentials") + .create(create) + .destroy(destroy) +} + +pub async fn create( + State(state): State<AppState>, + TypedHeader(Authorization(basic)): TypedHeader<Authorization<Basic>>, +) -> Result<(StatusCode, (AccessClaims, RefreshClaims)), Error> { + let salt = SaltString::generate(&mut OsRng); + let password_hash = Argon2::default().hash_password(basic.password().as_bytes(), &salt)?; + + let uuid = sqlx::query!( + "INSERT INTO credential (password_hash) VALUES ($1) RETURNING id", + password_hash.to_string() + ) + .fetch_optional(&state.pool) + .await? + .ok_or(Error::Registration)? + .id; + + let refresh = RefreshClaims::issue(uuid); + let access = refresh.refresh(); + + Ok((StatusCode::CREATED, (access, refresh))) +} + +pub async fn destroy(State(state): State<AppState>, Path(uuid): Path<Uuid>) -> Result<(), Error> { + let mut tx = state.pool.begin().await?; + let rows = sqlx::query!("DELETE FROM credential WHERE id = $1", uuid) + .execute(&mut *tx) + .await? + .rows_affected(); + + match rows { + 0 => Err(Error::UserNotFound), + 1 => { + tx.commit().await?; + Ok(()) + } + _ => { + tracing::error!("Delete query affected {rows} rows. This should not happen."); + Ok(()) + } + } +} |