summaryrefslogtreecommitdiffstats
path: root/src/auth
diff options
context:
space:
mode:
authorToby Vincent <tobyv@tobyvin.dev>2024-04-16 13:56:26 -0500
committerToby Vincent <tobyv@tobyvin.dev>2024-04-16 13:56:26 -0500
commite607eb77d4253adfb15c8a4ce08684e16ae96674 (patch)
tree921e6d002d9e3dc761f5d1bb7fea82abd2045919 /src/auth
parent469cbc20853bcae0e74922f16f7a969d1b7a9a67 (diff)
refactor(auth): move credential resource to module
Diffstat (limited to 'src/auth')
-rw-r--r--src/auth/credentials.rs66
-rw-r--r--src/auth/jwt.rs23
2 files changed, 89 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(())
+ }
+ }
+}
diff --git a/src/auth/jwt.rs b/src/auth/jwt.rs
index 9d70b94..0d7b593 100644
--- a/src/auth/jwt.rs
+++ b/src/auth/jwt.rs
@@ -40,3 +40,26 @@ impl Jwt {
jsonwebtoken::decode(token, &self.decoding, &self.validation).map_err(Into::into)
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::{
+ auth::AccessClaims,
+ tests::{setup_test_env, TestResult},
+ };
+
+ #[test]
+ fn test_jwt_encode_decode() -> TestResult {
+ setup_test_env();
+
+ let claims = AccessClaims::issue(uuid::Uuid::new_v4());
+ let token = JWT.encode(&claims)?;
+ let decoded = JWT.decode(&token)?.claims;
+
+ assert_eq!(claims, decoded);
+
+ Ok(())
+ }
+}