diff --git a/Cargo.lock b/Cargo.lock index 3c25fcb..d08d865 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -891,6 +891,7 @@ dependencies = [ "jwt", "rocket", "rocket_contrib", + "rust-crypto", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 8166fc4..5b2b0a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,5 @@ jwt = "0.4.0" diesel = { version = "1.4.3", features = ["sqlite"] } diesel_migrations = "1.4.0" dotenv = "0.9.0" -serde = { version = "1.0.104", features = ["derive"] } \ No newline at end of file +serde = { version = "1.0.104", features = ["derive"] } +rust-crypto = "0.2.36" \ No newline at end of file diff --git a/src/api.rs b/src/api.rs index 3e0e5a5..83c53a9 100644 --- a/src/api.rs +++ b/src/api.rs @@ -3,12 +3,13 @@ use crate::user; use rocket::http::Status; use rocket::response::status::Custom; use rocket_contrib::json::Json; -use serde::Serialize; +use serde::{Serialize, Deserialize}; use std::vec::Vec; pub fn routes() -> impl Into> { routes![ auth, + auth_sign_in, auth_params ] } @@ -43,8 +44,31 @@ struct AuthResult { #[post("/auth", format = "json", data = "")] fn auth(db: DbConn, new_user: Json) -> Custom> { match user::User::create(&db.0, &new_user) { - Ok(_) => success_resp(AuthResult { - token: "aaaa".to_string() + Ok(_) => _sign_in(db, &new_user.email, &new_user.password), + Err(user::UserOpError(e)) => + error_resp(Status::InternalServerError, vec![e]) + } +} + +#[derive(Deserialize)] +struct SignInParams { + email: String, + password: String +} + +#[post("/auth/sign_in", format = "json", data = "")] +fn auth_sign_in(db: DbConn, params: Json) -> Custom> { + _sign_in(db, ¶ms.email, ¶ms.password) +} + +// Shared logic for all interfaces that needs to do an automatic sign-in +fn _sign_in(db: DbConn, mail: &str, passwd: &str) -> Custom> { + // Try to find the user first + let res = user::User::find_user_by_email(&db, mail) + .and_then(|u| u.create_token(passwd)); + match res { + Ok(token) => success_resp(AuthResult { + token }), Err(user::UserOpError(e)) => error_resp(Status::InternalServerError, vec![e]) diff --git a/src/main.rs b/src/main.rs index cde045b..2b52a13 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ extern crate diesel_migrations; extern crate dotenv; #[macro_use] extern crate serde; +extern crate crypto; mod schema; mod api; diff --git a/src/user.rs b/src/user.rs index a46f418..8c864f2 100644 --- a/src/user.rs +++ b/src/user.rs @@ -3,6 +3,8 @@ use crate::schema::users::dsl::*; use diesel::prelude::*; use diesel::sqlite::SqliteConnection; use serde::Deserialize; +use std::env; +use std::time::{SystemTime, UNIX_EPOCH}; #[derive(Debug)] pub struct UserOpError(pub String); @@ -56,4 +58,28 @@ impl User { Result::Ok(results.remove(0)) // Take ownership, kill the stupid Vec } } + + // Create a JWT token for the current user if password matches + pub fn create_token(&self, passwd: &str) -> Result { + if passwd != self.password { + Err(UserOpError::new("Password mismatch")) + } else { + jwt::Token::new( + jwt::Header::default(), + jwt::Claims::new(jwt::Registered { + iss: None, + sub: Some(self.email.clone()), + exp: None, + aud: None, + nbf: Some(SystemTime::now().duration_since(UNIX_EPOCH) + .expect("wtf????").as_secs()), + iat: None, + jti: None + }) + ).signed(env::var("SFRS_JWT_SECRET") + .expect("Please have SFRS_JWT_SECRET set") + .as_bytes(), crypto::sha2::Sha256::new()) + .map_err(|_| UserOpError::new("Failed to generate token")) + } + } } \ No newline at end of file