JWT's for login and signup

This commit is contained in:
2024-12-10 04:37:14 -08:00
parent c013f6bad8
commit 58b9923eb9
6 changed files with 62 additions and 11 deletions

28
Cargo.lock generated
View File

@@ -74,8 +74,11 @@ dependencies = [
"axum", "axum",
"chrono", "chrono",
"dotenv", "dotenv",
"hmac",
"jwt",
"serde", "serde",
"serde_json", "serde_json",
"sha2",
"sqlx", "sqlx",
"tokio", "tokio",
"tower", "tower",
@@ -214,6 +217,12 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.22.1" version = "0.22.1"
@@ -835,6 +844,21 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "jwt"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f"
dependencies = [
"base64 0.13.1",
"crypto-common",
"digest",
"hmac",
"serde",
"serde_json",
"sha2",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.5.0" version = "1.5.0"
@@ -1649,7 +1673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64", "base64 0.22.1",
"bitflags", "bitflags",
"byteorder", "byteorder",
"bytes", "bytes",
@@ -1693,7 +1717,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64", "base64 0.22.1",
"bitflags", "bitflags",
"byteorder", "byteorder",
"chrono", "chrono",

View File

@@ -24,3 +24,6 @@ uuid = "1.11.0"
chrono = "0.4.39" chrono = "0.4.39"
dotenv = "0.15.0" dotenv = "0.15.0"
argon2 = "0.5.3" argon2 = "0.5.3"
jwt = "0.16.0"
hmac = "0.12.1"
sha2 = "0.10.8"

View File

@@ -56,10 +56,11 @@ async fn index() -> &'static str {
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
dotenv::dotenv().ok(); dotenv::dotenv().ok();
let db = db::db().await?;
let state = state::AppState { db: Arc::new(db) };
let port: u16 = std::env::var("PORT").unwrap_or_else(|_| "8080".to_string()).parse()?; let port: u16 = std::env::var("PORT").unwrap_or_else(|_| "8080".to_string()).parse()?;
let jwt_secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| "secret".to_string());
let db = db::db().await?;
let state = state::AppState { db: Arc::new(db), jwt_secret };
let (router, api) = OpenApiRouter::with_openapi(ApiDoc::openapi()) let (router, api) = OpenApiRouter::with_openapi(ApiDoc::openapi())
.routes(routes!(health_check)) .routes(routes!(health_check))

View File

@@ -3,6 +3,7 @@ use std::sync::Arc;
#[derive(Clone)] #[derive(Clone)]
pub struct AppState { pub struct AppState {
pub db: DB, pub db: DB,
pub jwt_secret: String,
} }
pub type DB = Arc<sqlx::Pool<sqlx::Postgres>>; pub type DB = Arc<sqlx::Pool<sqlx::Postgres>>;

View File

@@ -3,7 +3,11 @@ use argon2::{
password_hash::{PasswordHash, PasswordVerifier}, password_hash::{PasswordHash, PasswordVerifier},
Argon2, Argon2,
}; };
use hmac::{Hmac, Mac};
use jwt::SignWithKey;
use sha2::Sha256;
use sqlx::query; use sqlx::query;
use std::collections::BTreeMap;
#[derive(Serialize, Deserialize, ToSchema)] #[derive(Serialize, Deserialize, ToSchema)]
pub struct LoginBody { pub struct LoginBody {
@@ -36,10 +40,14 @@ pub async fn login(
return Err(AppError::Error(Errors::Unauthorized)); return Err(AppError::Error(Errors::Unauthorized));
} }
// technically I should be using a JWT and returning it to the client let key: Hmac<Sha256> =
// since this will be a mobile app. Hmac::new_from_slice(state.jwt_secret.as_bytes()).context("Failed to create HMAC")?;
let mut claims = BTreeMap::new();
claims.insert("id", user.id.to_string());
claims.insert("username", user.username);
claims.insert("real_name", user.real_name);
claims.insert("email", user.email);
let token_str = claims.sign_with_key(&key).context("Failed to sign JWT")?;
// let jwt = ... Ok(token_str)
Ok(user.id.to_string())
} }

View File

@@ -6,6 +6,11 @@ use argon2::{
Argon2, Argon2,
}; };
use hmac::{Hmac, Mac};
use jwt::SignWithKey;
use sha2::Sha256;
use std::collections::BTreeMap;
#[derive(Serialize, Deserialize, ToSchema)] #[derive(Serialize, Deserialize, ToSchema)]
pub struct SignupBody { pub struct SignupBody {
real_name: String, real_name: String,
@@ -40,5 +45,14 @@ pub async fn signup(
.fetch_one(&*state.db) .fetch_one(&*state.db)
.await?; .await?;
Ok(user.id.to_string()) let key: Hmac<Sha256> =
Hmac::new_from_slice(state.jwt_secret.as_bytes()).context("Failed to create HMAC")?;
let mut claims = BTreeMap::new();
claims.insert("id", user.id.to_string());
claims.insert("username", user.username);
claims.insert("real_name", user.real_name);
claims.insert("email", user.email);
let token_str = claims.sign_with_key(&key).context("Failed to sign JWT")?;
Ok(token_str)
} }