From e1856f9e6d81a7d7ff54f3b7fb56e1f811921ea2 Mon Sep 17 00:00:00 2001 From: John Lewis Date: Wed, 31 Jan 2024 16:35:49 -0600 Subject: [PATCH] basic auth backend complete but unused --- engine/Cargo.lock | 10 ++++++ engine/crates/auth/Cargo.toml | 4 ++- engine/crates/auth/src/lib.rs | 54 +++++++++++++++++++--------- engine/crates/clients/src/surreal.rs | 16 +++++++-- justfile | 2 +- 5 files changed, 64 insertions(+), 22 deletions(-) diff --git a/engine/Cargo.lock b/engine/Cargo.lock index f87084c..9d30fe2 100644 --- a/engine/Cargo.lock +++ b/engine/Cargo.lock @@ -219,6 +219,7 @@ dependencies = [ "axum-login", "clients", "color-eyre", + "redact", "serde", "surrealdb", "thiserror", @@ -2265,6 +2266,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redact" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbcc931183921ce8ec7abb78e335d8e0dbdd86ee237bd991bc022f7b79251168" +dependencies = [ + "serde", +] + [[package]] name = "redox_syscall" version = "0.4.1" diff --git a/engine/crates/auth/Cargo.toml b/engine/crates/auth/Cargo.toml index 1281bd2..0f0a954 100644 --- a/engine/crates/auth/Cargo.toml +++ b/engine/crates/auth/Cargo.toml @@ -7,10 +7,12 @@ edition = "2021" [dependencies] axum-login = "0.13.1" +async-trait = "0.1.77" +redact = { version = "0.1.8", features = [ "serde" ] } + serde.workspace = true color-eyre.workspace = true thiserror.workspace = true surrealdb.workspace = true clients = { path = "../clients" } -async-trait = "0.1.77" diff --git a/engine/crates/auth/src/lib.rs b/engine/crates/auth/src/lib.rs index 214f233..7647fa1 100644 --- a/engine/crates/auth/src/lib.rs +++ b/engine/crates/auth/src/lib.rs @@ -1,21 +1,28 @@ use axum_login::{AuthUser, AuthnBackend, UserId}; +use redact::Secret; use serde::{Deserialize, Serialize}; use surrealdb::sql::Thing; -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Deserialize, Debug)] pub struct AuthenticatedUser { - pub id: Thing, - pub username: String, - pub password: String, + pub id: Thing, + pw_hash: Secret, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct User { + pub id: Thing, + pub name: String, + pub email: String, + pub pw_hash: Secret, } -impl std::fmt::Debug for AuthenticatedUser { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("AuthenticatedUser") - .field("id", &self.id) - .field("username", &self.username) - .field("password", &"[redacted]") - .finish() +impl From for AuthenticatedUser { + fn from(user: User) -> Self { + Self { + id: user.id, + pw_hash: user.pw_hash, + } } } @@ -23,12 +30,14 @@ impl AuthUser for AuthenticatedUser { type Id = Thing; fn id(&self) -> Self::Id { self.id.clone() } - fn session_auth_hash(&self) -> &[u8] { self.password.as_bytes() } + fn session_auth_hash(&self) -> &[u8] { + self.pw_hash.expose_secret().as_bytes() + } } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Credentials { - pub username: String, + pub email: String, pub password: String, pub next: Option, } @@ -50,21 +59,32 @@ impl Backend { impl AuthnBackend for Backend { type User = AuthenticatedUser; type Credentials = Credentials; - type Error = std::convert::Infallible; + type Error = surrealdb::Error; async fn authenticate( &self, credentials: Self::Credentials, ) -> Result, Self::Error> { - let surreal_client = &self.surreal_client; + let user: Option = (*self.surreal_client) + .query( + "SELECT id FROM users WHERE email = $email AND \ + crypto::argon2::compare(password, $password))", + ) + .bind(("email", &credentials.email)) + .bind(("password", &credentials.password)) + .await? + .take(0)?; - Ok(None) + Ok(user.map(AuthenticatedUser::from)) } async fn get_user( &self, user_id: &UserId, ) -> Result, Self::Error> { - Ok(None) + let user: Option = (*self.surreal_client).select(user_id).await?; + Ok(user.map(AuthenticatedUser::from)) } } + +pub type AuthSession = axum_login::AuthSession; diff --git a/engine/crates/clients/src/surreal.rs b/engine/crates/clients/src/surreal.rs index a79e37d..7214aa0 100644 --- a/engine/crates/clients/src/surreal.rs +++ b/engine/crates/clients/src/surreal.rs @@ -24,7 +24,17 @@ impl SurrealRootClient { ) .await .wrap_err("Could not connect to SurrealDB endpoint")?; - client + + let client = Self { client }; + client.sign_in_as_root().await?; + + Ok(client) + } + + /// Signs in as root. + pub async fn sign_in_as_root(&self) -> Result<()> { + self + .client .signin(Root { username: &std::env::var("SURREALDB_ROOT_USER") .wrap_err("Could not find env var \"SURREALDB_ROOT_USER\"")?, @@ -32,8 +42,8 @@ impl SurrealRootClient { .wrap_err("Could not find env var \"SURREALDB_ROOT_PASS\"")?, }) .await - .wrap_err("Could not sign in to SurrealDB as root")?; - Ok(Self { client }) + .wrap_err("Could not sign in to SurrealDB as root") + .map(|_| ()) } } diff --git a/justfile b/justfile index f56d3c6..31d1b23 100644 --- a/justfile +++ b/justfile @@ -6,4 +6,4 @@ surreal: wipe-surreal: rm -rf /tmp/surreal_data apply-surreal: - cd engine/migrations && surrealdb-migrations apply --username $SURREALDB_ROOT_USER --password $SURREALDB_ROOT_PASS + cd engine/migrations && surrealdb-migrations apply --username $SURREALDB_ROOT_USER --password $SURREALDB_ROOT_PASS --ns main --db main