feat: create code for login
This commit is contained in:
parent
a3d8e07c26
commit
9ad50fd4f0
1
backend/.env.example
Normal file
1
backend/.env.example
Normal file
@ -0,0 +1 @@
|
|||||||
|
DATABASE_URL=sqlite://./db.sqlite
|
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
db.sqlite
|
db.sqlite
|
||||||
|
.env
|
||||||
|
|
||||||
|
131
backend/Cargo.lock
generated
131
backend/Cargo.lock
generated
@ -80,6 +80,67 @@ version = "0.2.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "argon2"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"blake2",
|
||||||
|
"cpufeatures",
|
||||||
|
"password-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-stream"
|
name = "async-stream"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
@ -147,6 +208,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
|||||||
name = "backend"
|
name = "backend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"argon2",
|
||||||
|
"env_logger",
|
||||||
|
"log",
|
||||||
"rocket",
|
"rocket",
|
||||||
"rocket_db_pools",
|
"rocket_db_pools",
|
||||||
"serde",
|
"serde",
|
||||||
@ -202,6 +266,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake2"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
@ -254,6 +327,12 @@ dependencies = [
|
|||||||
"inout",
|
"inout",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
@ -436,6 +515,29 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_filter"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.11.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"env_filter",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -801,6 +903,12 @@ version = "1.0.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.32"
|
version = "0.14.32"
|
||||||
@ -1001,6 +1109,12 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.14"
|
version = "1.0.14"
|
||||||
@ -1291,6 +1405,17 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "password-hash"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
@ -2552,6 +2677,12 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -4,6 +4,9 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
argon2 = "0.5.3"
|
||||||
|
env_logger = "0.11.6"
|
||||||
|
log = "0.4.22"
|
||||||
rocket = { version = "0.5.1", features = ["json", "secrets"] }
|
rocket = { version = "0.5.1", features = ["json", "secrets"] }
|
||||||
rocket_db_pools = { version = "0.2.0", features = ["sqlx_sqlite"] }
|
rocket_db_pools = { version = "0.2.0", features = ["sqlx_sqlite"] }
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
|
@ -1 +1,2 @@
|
|||||||
-- Add migration script here
|
-- Add migration script here
|
||||||
|
DROP TABLE monke;
|
6
backend/migrations/20241231030841_init.up.sql
Normal file
6
backend/migrations/20241231030841_init.up.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
-- Add migration script here
|
||||||
|
CREATE TABLE monke (
|
||||||
|
monke_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
monke_name TEXT NOT NULL,
|
||||||
|
monke_password TEXT NOT NULL
|
||||||
|
);
|
@ -4,44 +4,27 @@ use rocket::{
|
|||||||
request::{FromRequest, Outcome},
|
request::{FromRequest, Outcome},
|
||||||
Request, Rocket,
|
Request, Rocket,
|
||||||
};
|
};
|
||||||
use rocket_db_pools::sqlx::{self, Row};
|
use rocket_db_pools::sqlx::{self};
|
||||||
use rocket_db_pools::{Connection, Database};
|
use rocket_db_pools::Database;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
|
mod modules;
|
||||||
|
|
||||||
// so i was trying out claude.ai, and as a joke i talked to it as monke,
|
// so i was trying out claude.ai, and as a joke i talked to it as monke,
|
||||||
// and it answered like monke. so i thought it would be funny to,
|
// and it answered like monke. so i thought it would be funny to,
|
||||||
// instead of plain, boring "User", just use "Monke" :D
|
// instead of plain, boring "User", just use "Monke" :D
|
||||||
|
|
||||||
/// Stores info about a Monke
|
/// Stores info about a Monke
|
||||||
struct Monke {
|
struct Monke {
|
||||||
pub user_id: String,
|
pub monke_id: usize,
|
||||||
|
pub monke_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Database)]
|
#[derive(Database)]
|
||||||
#[database("main")]
|
#[database("main")]
|
||||||
struct MainDb(sqlx::SqlitePool);
|
pub struct MainDb(sqlx::SqlitePool);
|
||||||
|
|
||||||
#[rocket::async_trait]
|
|
||||||
impl fairing::Fairing for MainDb {
|
|
||||||
fn info(&self) -> fairing::Info {
|
|
||||||
fairing::Info {
|
|
||||||
name: "Database Migrations",
|
|
||||||
kind: fairing::Kind::Liftoff,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_liftoff(&self, _rocket: &Rocket<rocket::Orbit>) {
|
|
||||||
let db = self;
|
|
||||||
|
|
||||||
// monke run migrations here
|
|
||||||
sqlx::migrate!()
|
|
||||||
.run(&**db)
|
|
||||||
.await
|
|
||||||
.expect("Failed to migrate db");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
impl<'r> FromRequest<'r> for Monke {
|
impl<'r> FromRequest<'r> for Monke {
|
||||||
@ -61,7 +44,8 @@ impl<'r> FromRequest<'r> for Monke {
|
|||||||
Outcome::Error((Status::Unauthorized, ()))
|
Outcome::Error((Status::Unauthorized, ()))
|
||||||
} else {
|
} else {
|
||||||
Outcome::Success(Monke {
|
Outcome::Success(Monke {
|
||||||
user_id: cookie.value().to_string(),
|
monke_id: cookie.value().parse().unwrap(),
|
||||||
|
monke_name: "Test".into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,5 +63,5 @@ fn index() -> &'static str {
|
|||||||
async fn rocket() -> _ {
|
async fn rocket() -> _ {
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.attach(MainDb::init())
|
.attach(MainDb::init())
|
||||||
.mount("/", routes![index])
|
.mount("/api", routes![index, modules::auth::login])
|
||||||
}
|
}
|
||||||
|
83
backend/src/modules/auth/mod.rs
Normal file
83
backend/src/modules/auth/mod.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
use argon2::{password_hash, Argon2, PasswordHash, PasswordVerifier};
|
||||||
|
use rocket::{
|
||||||
|
http::{CookieJar, Status},
|
||||||
|
serde::json::Json,
|
||||||
|
};
|
||||||
|
use rocket_db_pools::Connection;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::MainDb;
|
||||||
|
|
||||||
|
const COOKIE_INDEX: &'static str = "monke_secret";
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct LoginCredentials {
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApiResponse = Json<Result<(), String>>;
|
||||||
|
|
||||||
|
#[post("/login", data = "<credentials>")]
|
||||||
|
pub async fn login(
|
||||||
|
credentials: Json<LoginCredentials>,
|
||||||
|
mut db: Connection<MainDb>,
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
) -> (Status, ApiResponse) {
|
||||||
|
// check them db
|
||||||
|
let result = sqlx::query!(
|
||||||
|
"SELECT * FROM monke WHERE monke_name=?",
|
||||||
|
credentials.username
|
||||||
|
)
|
||||||
|
.fetch_one(&mut **db)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let result = match result {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(sqlx::Error::RowNotFound) => {
|
||||||
|
return (Status::BadRequest, Json(Err("Invalid credentials".into())));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("error fetching db: {:?}", e);
|
||||||
|
|
||||||
|
return (
|
||||||
|
Status::InternalServerError,
|
||||||
|
Json(Err("Server error".into())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// check them credentials
|
||||||
|
let parsed_hash = match PasswordHash::new(&result.monke_password) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("argon2 hash parse error: {:?}", e);
|
||||||
|
return (
|
||||||
|
Status::InternalServerError,
|
||||||
|
Json(Err(String::from("Server error"))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match Argon2::default().verify_password(credentials.password.as_bytes(), &parsed_hash) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(password_hash::Error::Password) => {
|
||||||
|
return (
|
||||||
|
Status::BadRequest,
|
||||||
|
Json(Err(String::from("Invalid credentials"))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("error verifying argon2 password: {:?}", e);
|
||||||
|
return (
|
||||||
|
Status::InternalServerError,
|
||||||
|
Json(Err(String::from("Server error"))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create cookie
|
||||||
|
cookies.add_private((COOKIE_INDEX, result.monke_id.to_string()));
|
||||||
|
|
||||||
|
// send ok
|
||||||
|
return (Status::Ok, Json(Ok(())));
|
||||||
|
}
|
1
backend/src/modules/mod.rs
Normal file
1
backend/src/modules/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod auth;
|
Loading…
Reference in New Issue
Block a user