summaryrefslogtreecommitdiffstats
path: root/src/auth/credentials.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/auth/credentials.rs')
-rw-r--r--src/auth/credentials.rs66
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(())
+ }
+ }
+}