diff --git a/.env b/.env index ab91f2c..8554d4a 100644 --- a/.env +++ b/.env @@ -1 +1,2 @@ -DATABASE_URL=postgres://alisa:alisa@localhost/alisa \ No newline at end of file +DATABASE_URL=postgres://alisa:alisa@localhost/alisa +TOKEN_SECRET=secret \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 6c73b0d..facef49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -573,6 +573,7 @@ dependencies = [ "argon2", "dotenvy", "entity", + "jsonwebtoken", "migration", "sea-orm", "serde", @@ -1284,8 +1285,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1546,6 +1549,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +dependencies = [ + "base64 0.21.7", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -1884,6 +1902,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -2620,6 +2648,18 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time", +] + [[package]] name = "slab" version = "0.4.9" diff --git a/crates/backend/Cargo.toml b/crates/backend/Cargo.toml index 61fb5b2..c13d527 100644 --- a/crates/backend/Cargo.toml +++ b/crates/backend/Cargo.toml @@ -17,3 +17,4 @@ sea-orm = { version = "0.12", features = [ "with-uuid", ] } dotenvy = "*" +jsonwebtoken = "*" diff --git a/crates/backend/src/auth.rs b/crates/backend/src/auth.rs new file mode 100644 index 0000000..35129ee --- /dev/null +++ b/crates/backend/src/auth.rs @@ -0,0 +1,26 @@ +use jsonwebtoken::{EncodingKey, Header, Validation}; +use migration::token; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +#[derive(Deserialize, Serialize)] +struct Claims { + sub: Uuid, + name: String, +} + +pub fn create_jwt( + user: entity::user::Model, + key: &EncodingKey, +) -> Result { + let claims = Claims { + sub: user.id, + name: user.name, + }; + jsonwebtoken::encode(&Header::default(), &claims, key) +} + +pub fn verify(token: &str) { + let validation = Validation::new(jsonwebtoken::Algorithm::HS256); + // jsonwebtoken::decode(token, , validation) +} diff --git a/crates/backend/src/controller/auth.rs b/crates/backend/src/controller/auth.rs index de0e326..8ec2d10 100644 --- a/crates/backend/src/controller/auth.rs +++ b/crates/backend/src/controller/auth.rs @@ -1,14 +1,12 @@ use actix_web::{ error::{ErrorBadRequest, ErrorInternalServerError}, - web, Responder, + web, HttpResponse, Responder, }; use argon2::{Argon2, PasswordHash, PasswordVerifier}; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; use serde::Deserialize; -use crate::AppState; - -use super::user::UserWithoutPassword; +use crate::{auth::create_jwt, AppState}; #[derive(Deserialize)] pub struct LoginRequest { @@ -24,6 +22,7 @@ impl AuthController { login: web::Json, ) -> actix_web::Result { let db = &state.db; + let jwt_secret = &state.secret; let login = login.into_inner(); let user = entity::user::Entity::find() @@ -39,6 +38,7 @@ impl AuthController { .verify_password(login.password.as_bytes(), &parsed_hash) .map_err(ErrorBadRequest)?; - Ok(web::Json(UserWithoutPassword::from(user))) + let jwt = create_jwt(user, jwt_secret).map_err(ErrorInternalServerError)?; + Ok(HttpResponse::Ok().body(jwt)) } } diff --git a/crates/backend/src/main.rs b/crates/backend/src/main.rs index b779476..313ca8e 100644 --- a/crates/backend/src/main.rs +++ b/crates/backend/src/main.rs @@ -1,15 +1,18 @@ use actix_web::{web, App, HttpServer}; +use jsonwebtoken::EncodingKey; use migration::MigratorTrait; use sea_orm::{Database, DatabaseConnection}; use std::env; use routes::config; +mod auth; mod controller; mod routes; #[derive(Clone)] struct AppState { db: DatabaseConnection, + secret: EncodingKey, } #[actix_web::main] @@ -20,6 +23,7 @@ async fn main() -> std::io::Result<()> { dotenvy::dotenv().ok(); let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); + let jwt_secret = env::var("TOKEN_SECRET").expect("TOKEN_SECRET must be set"); let conn = Database::connect(&db_url) .await @@ -31,7 +35,10 @@ async fn main() -> std::io::Result<()> { .expect("Running migrations failed"); println!("Finished running migrations"); - let state = AppState { db: conn }; + let state = AppState { + db: conn, + secret: EncodingKey::from_secret(jwt_secret.as_bytes()), + }; println!("Listening for connections..."); HttpServer::new(move || {