From 2924e385925f96b3bd68ea0fa30d380861243a84 Mon Sep 17 00:00:00 2001 From: MedzikUser Date: Sat, 23 Apr 2022 23:48:20 +0200 Subject: [PATCH] server (http): add jwt token validator and route `/auth/whoami` --- Cargo.lock | 13 +++++++++++++ database/src/sqlite.rs | 18 ++++++++++++++++++ server/Cargo.toml | 2 ++ server/src/auth/mod.rs | 2 ++ server/src/auth/whoami.rs | 35 +++++++++++++++++++++++++++++++++++ server/src/middleware/auth.rs | 9 +++++++++ types/src/auth/mod.rs | 1 + types/src/auth/whoami.rs | 6 ++++++ types/src/errors/auth.rs | 3 +++ types/src/errors/mod.rs | 1 + 10 files changed, 90 insertions(+) create mode 100644 server/src/auth/whoami.rs create mode 100644 types/src/auth/whoami.rs diff --git a/Cargo.lock b/Cargo.lock index 4298345..397c33f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,6 +120,17 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-auth" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78cc399f2af2dd7adf88e0fcc0e21dbf730258c1b34785f47816ff224238f74" +dependencies = [ + "axum", + "base64", + "http", +] + [[package]] name = "axum-core" version = "0.2.2" @@ -609,9 +620,11 @@ name = "homedisk-server" version = "0.0.0" dependencies = [ "axum", + "axum-auth", "homedisk-database", "homedisk-types", "hyper", + "jsonwebtoken", "log", "rust_utilities", "serde", diff --git a/database/src/sqlite.rs b/database/src/sqlite.rs index 796058a..7fd02b5 100644 --- a/database/src/sqlite.rs +++ b/database/src/sqlite.rs @@ -66,6 +66,24 @@ impl Database { password, }) } + + pub async fn find_user_by_id(&self, id: String) -> Result { + let query = sqlx::query_as::<_, User>("SELECT * FROM user WHERE id = ?").bind(id); + + let mut stream = self.conn.fetch(query); + + let row = stream.try_next().await?.ok_or(Error::UserNotFound)?; + + let id = row.try_get("id")?; + let username = row.try_get("username")?; + let password = row.try_get("password")?; + + Ok(User { + id, + username, + password, + }) + } } #[cfg(test)] diff --git a/server/Cargo.toml b/server/Cargo.toml index 282d7ca..a4c2656 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -13,3 +13,5 @@ hyper = { version = "0.14.18", features = ["full"] } rust_utilities = { version = "0.2.0", features = ["jsonwebtoken"] } homedisk-database = { path = "../database" } homedisk-types = { path = "../types", features = ["axum"] } +axum-auth = "0.2.0" +jsonwebtoken = "8.1.0" diff --git a/server/src/auth/mod.rs b/server/src/auth/mod.rs index 82cba9a..b1bc636 100644 --- a/server/src/auth/mod.rs +++ b/server/src/auth/mod.rs @@ -1,5 +1,6 @@ pub mod login; pub mod register; +pub mod whoami; pub fn app() -> axum::Router { use axum::routing::post; @@ -7,4 +8,5 @@ pub fn app() -> axum::Router { axum::Router::new() .route("/login", post(login::handle)) .route("/register", post(register::handle)) + .route("/whoami", post(whoami::handle)) } diff --git a/server/src/auth/whoami.rs b/server/src/auth/whoami.rs new file mode 100644 index 0000000..d26da78 --- /dev/null +++ b/server/src/auth/whoami.rs @@ -0,0 +1,35 @@ +use axum::{Extension, Json}; +use axum_auth::AuthBearer; +use homedisk_database::{Database, Error}; +use homedisk_types::{ + auth::whoami::Response, + config::types::Config, + errors::{AuthError, ServerError}, +}; + +use crate::middleware::validate_jwt; + +pub async fn handle( + db: Extension, + config: Extension, + AuthBearer(token): AuthBearer, +) -> Result, ServerError> { + let token = validate_jwt(config.jwt.secret.as_bytes(), &token)?; + + let response = match db.find_user_by_id(token.claims.sub).await { + Ok(res) => Response { + username: res.username, + }, + + Err(err) => match err { + Error::UserNotFound => return Err(ServerError::AuthError(AuthError::UserNotFound)), + _ => { + return Err(ServerError::AuthError(AuthError::UnknowError( + err.to_string(), + ))) + } + }, + }; + + Ok(Json(response)) +} diff --git a/server/src/middleware/auth.rs b/server/src/middleware/auth.rs index 8b13789..3c1a351 100644 --- a/server/src/middleware/auth.rs +++ b/server/src/middleware/auth.rs @@ -1 +1,10 @@ +use homedisk_types::errors::{AuthError, ServerError}; +use jsonwebtoken::TokenData; +use rust_utilities::crypto::jsonwebtoken::{Claims, Token}; +pub fn validate_jwt(secret: &[u8], token: &str) -> Result, ServerError> { + match Token::decode(secret, token.to_string()) { + Ok(claims) => Ok(claims), + Err(_) => Err(ServerError::AuthError(AuthError::InvalidToken)), + } +} diff --git a/types/src/auth/mod.rs b/types/src/auth/mod.rs index 320cbbb..1e2917a 100644 --- a/types/src/auth/mod.rs +++ b/types/src/auth/mod.rs @@ -1 +1,2 @@ pub mod login; +pub mod whoami; diff --git a/types/src/auth/whoami.rs b/types/src/auth/whoami.rs new file mode 100644 index 0000000..9335599 --- /dev/null +++ b/types/src/auth/whoami.rs @@ -0,0 +1,6 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Response { + pub username: String, +} diff --git a/types/src/errors/auth.rs b/types/src/errors/auth.rs index 2d0032f..162cc48 100644 --- a/types/src/errors/auth.rs +++ b/types/src/errors/auth.rs @@ -11,6 +11,9 @@ pub enum Error { #[error("generate jwt token")] TokenGenerate, + #[error("invalid jwt token")] + InvalidToken, + #[error("unknow error")] UnknowError(String), } diff --git a/types/src/errors/mod.rs b/types/src/errors/mod.rs index 1a31f2f..8f643c9 100644 --- a/types/src/errors/mod.rs +++ b/types/src/errors/mod.rs @@ -41,6 +41,7 @@ impl axum::response::IntoResponse for ServerError { AuthError::UserAlreadyExists => StatusCode::NOT_ACCEPTABLE, AuthError::TokenGenerate => StatusCode::INTERNAL_SERVER_ERROR, AuthError::UnknowError(_) => StatusCode::INTERNAL_SERVER_ERROR, + AuthError::InvalidToken => StatusCode::BAD_REQUEST, }, Self::MissingJsonContentType => StatusCode::BAD_REQUEST,