From c831da6a14b11b23765552f96513997968cdeeea Mon Sep 17 00:00:00 2001
From: Paul Makles <paulmakles@gmail.com>
Date: Sat, 25 Jan 2020 11:08:01 +0000
Subject: [PATCH] Add /login route.

---
 src/routes/account.rs | 71 +++++++++++++++++++++++++++++++++++++------
 src/routes/mod.rs     |  2 +-
 2 files changed, 63 insertions(+), 10 deletions(-)

diff --git a/src/routes/account.rs b/src/routes/account.rs
index 557704d..689a2f1 100644
--- a/src/routes/account.rs
+++ b/src/routes/account.rs
@@ -11,6 +11,13 @@ use bcrypt::{ hash, verify };
 use chrono::prelude::*;
 use ulid::Ulid;
 
+fn gen_token(l: usize) -> String {
+	rand::thread_rng()
+		.sample_iter(&Alphanumeric)
+		.take(l)
+		.collect::<String>()
+}
+
 #[get("/")]
 pub fn root(user: User) -> String {
 	let User ( id, username, _doc ) = user;
@@ -65,16 +72,15 @@ pub fn create(info: Json<Create>) -> JsonValue {
 	}
 
 	if let Ok(hashed) = hash(info.password.clone(), 10) {
-		let code = rand::thread_rng()
-			.sample_iter(&Alphanumeric)
-			.take(48)
-			.collect::<String>();
+		let access_token = gen_token(64);
+		let code = gen_token(48);
 
 		match col.insert_one(doc! {
 			"_id": Ulid::new().to_string(),
 			"email": info.email.clone(),
 			"username": info.username.clone(),
 			"password": hashed,
+			"access_token": access_token,
 			"email_verification": {
 				"verified": false,
 				"target": info.email.clone(),
@@ -182,11 +188,7 @@ pub fn resend_email(info: Json<Resend>) -> JsonValue {
 					"error": "Hit rate limit! Please try again in a minute or so."
 				})
 			} else {
-				let code = rand::thread_rng()
-					.sample_iter(&Alphanumeric)
-					.take(48)
-					.collect::<String>();
-
+				let code = gen_token(48);
 				col.update_one(
 					doc! { "_id": u.get_str("_id").expect("Failed to retrieve user id.") },
 					doc! {
@@ -219,3 +221,54 @@ pub fn resend_email(info: Json<Resend>) -> JsonValue {
 			})
 		}
 }
+
+#[derive(Serialize, Deserialize)]
+pub struct Login {
+	email: String,
+	password: String,
+}
+
+/// login to a Revolt account
+/// (1) find user by email
+/// (2) verify password
+/// (3) return access token
+#[post("/login", data = "<info>")]
+pub fn login(info: Json<Login>) -> JsonValue {
+	let col = database::get_db().collection("users");
+
+	if let Some(u) =
+		col.find_one(doc! { "email": info.email.clone() }, None).expect("Failed user lookup") {
+			match verify(info.password.clone(), u.get_str("password").expect("Missing password in user object!"))
+				.expect("Failed to check hash of password.") {
+					true => {
+						let token =
+							match u.get_str("access_token") {
+								Ok(t) => t.to_string(),
+								Err(_) => {
+									let token = gen_token(64);
+									col.update_one(
+										doc! { "_id": u.get_str("_id").expect("Missing id in user object!") },
+										doc! { "$set": { "access_token": token.clone() } },
+										None
+									).expect("Failed to update user object");
+									token
+								}
+							};
+
+						json!({
+							"success": true,
+							"access_token": token
+						})
+					},
+					false => json!({
+						"success": false,
+						"error": "Invalid password."
+					})
+				}
+		} else {
+			json!({
+				"success": false,
+				"error": "Email is not registered.",
+			})
+		}
+}
diff --git a/src/routes/mod.rs b/src/routes/mod.rs
index 3d871f3..14f1a24 100644
--- a/src/routes/mod.rs
+++ b/src/routes/mod.rs
@@ -4,5 +4,5 @@ mod account;
 
 pub fn mount(rocket: Rocket) -> Rocket {
 	rocket
-		.mount("/api/account", routes![ account::root, account::create, account::verify_email, account::resend_email ])
+		.mount("/api/account", routes![ account::root, account::create, account::verify_email, account::resend_email, account::login ])
 }
-- 
GitLab