Check user/pw against list of known users w/hashed pw
This commit is contained in:
parent
c21054814c
commit
cbd2792ad3
46
Cargo.lock
generated
46
Cargo.lock
generated
@ -102,6 +102,18 @@ version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
|
||||
|
||||
[[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]]
|
||||
name = "async-compression"
|
||||
version = "0.4.13"
|
||||
@ -234,6 +246,12 @@ version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
@ -249,6 +267,15 @@ version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@ -1096,6 +1123,13 @@ dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash_pw"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.0"
|
||||
@ -1471,6 +1505,17 @@ dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
@ -1769,6 +1814,7 @@ dependencies = [
|
||||
name = "server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"axum",
|
||||
"axum-login",
|
||||
"ciborium",
|
||||
|
@ -2,6 +2,6 @@
|
||||
resolver = "2"
|
||||
|
||||
members = [ "common",
|
||||
"frontend",
|
||||
"frontend", "hash_pw",
|
||||
"server",
|
||||
]
|
||||
|
7
hash_pw/Cargo.toml
Normal file
7
hash_pw/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "hash_pw"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
argon2 = "0.5.3"
|
16
hash_pw/src/main.rs
Normal file
16
hash_pw/src/main.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use std::io::{stdin, stdout, Write};
|
||||
|
||||
use argon2::{password_hash::{rand_core::OsRng, SaltString}, Argon2, PasswordHasher};
|
||||
|
||||
fn main() {
|
||||
print!("Enter password:");
|
||||
stdout().flush().unwrap();
|
||||
let mut pw_buf = String::new();
|
||||
stdin().read_line(&mut pw_buf).unwrap();
|
||||
let password = pw_buf.trim_end();
|
||||
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
let argon2 = Argon2::default();
|
||||
let pw_hash = argon2.hash_password(password.as_bytes(), &salt).unwrap().to_string();
|
||||
println!("{pw_hash}");
|
||||
}
|
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
argon2 = "0.5.3"
|
||||
axum = { version = "0.7.7", features = ["ws"] }
|
||||
axum-login = "0.16.0"
|
||||
ciborium = "0.2.2"
|
||||
|
@ -1,3 +1,4 @@
|
||||
use argon2::{Argon2, PasswordHash, PasswordVerifier};
|
||||
use axum::body::Body;
|
||||
use axum::extract::ws::{self, CloseFrame, Message, WebSocket};
|
||||
use axum::extract::{Request, State, WebSocketUpgrade};
|
||||
@ -13,6 +14,7 @@ use common::{ChatMessage, LoggedInResponse, LoginRequest, LoginResponse};
|
||||
use futures::stream::SplitSink;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use slab::Slab;
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
@ -66,43 +68,67 @@ impl ServState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct DummyAuthUser;
|
||||
//const USERS: HashMap<String, User> = {
|
||||
// let mut map = HashMap::new();
|
||||
// map.insert("pjht".to_string(), User {
|
||||
// username: "pjht".to_string(),
|
||||
// pw_hash: "$argon2id$v=19$m=19456,t=2,p=1$miwLkAOUyJa7NxWX9ueBJQ$w43pjFVBqnqRvWWiPaW2cbhlxk0Dq5sdYjmy4I+Yh+U".to_string(),
|
||||
// id: 0,
|
||||
// }).unwrap();
|
||||
//map
|
||||
//};
|
||||
|
||||
impl AuthUser for DummyAuthUser {
|
||||
struct UserCreds {
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct User {
|
||||
username: String,
|
||||
pw_hash: String,
|
||||
}
|
||||
|
||||
impl AuthUser for User {
|
||||
type Id = String;
|
||||
|
||||
fn id(&self) -> Self::Id {
|
||||
"pjht".to_string()
|
||||
self.username.clone()
|
||||
}
|
||||
|
||||
fn session_auth_hash(&self) -> &[u8] {
|
||||
&[0]
|
||||
self.pw_hash.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct DummyAuthBackend;
|
||||
struct AuthBackend {
|
||||
users: HashMap<String, User>
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AuthnBackend for DummyAuthBackend {
|
||||
type User = DummyAuthUser;
|
||||
type Credentials = ();
|
||||
impl AuthnBackend for AuthBackend {
|
||||
type User = User;
|
||||
type Credentials = UserCreds;
|
||||
type Error = std::convert::Infallible;
|
||||
|
||||
async fn authenticate(
|
||||
&self,
|
||||
_creds: Self::Credentials,
|
||||
creds: Self::Credentials,
|
||||
) -> Result<Option<Self::User>, Self::Error> {
|
||||
Ok(Some(DummyAuthUser))
|
||||
Ok((|| {
|
||||
let user = self.users.get(&creds.username)?;
|
||||
let pw_hash = PasswordHash::new(&user.pw_hash).unwrap();
|
||||
if Argon2::default().verify_password(creds.password.as_bytes(), &pw_hash).is_ok() {
|
||||
Some(user.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})())
|
||||
}
|
||||
|
||||
async fn get_user(&self, user_id: &UserId<Self>) -> Result<Option<Self::User>, Self::Error> {
|
||||
if user_id == "pjht" {
|
||||
Ok(Some(DummyAuthUser))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
Ok(self.users.get(user_id).cloned())
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,29 +151,26 @@ async fn serve_index(static_dir: &Path) -> Response {
|
||||
}
|
||||
|
||||
async fn login(
|
||||
mut auth_session: AuthSession<DummyAuthBackend>,
|
||||
mut auth_session: AuthSession<AuthBackend>,
|
||||
Json(req): Json<LoginRequest>,
|
||||
) -> Response {
|
||||
if req.username != "pjht" || req.password != "123456" {
|
||||
return Json(LoginResponse(Err(
|
||||
"Invalid username or password".to_string()
|
||||
)))
|
||||
.into_response();
|
||||
}
|
||||
if auth_session.login(&DummyAuthUser).await.is_err() {
|
||||
let Some(user) = auth_session.authenticate(UserCreds {username: req.username, password: req.password}).await.unwrap() else {
|
||||
return Json(LoginResponse(Err("Invalid username or password".to_string()))).into_response();
|
||||
};
|
||||
if auth_session.login(&user).await.is_err() {
|
||||
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
|
||||
}
|
||||
Json(LoginResponse(Ok(()))).into_response()
|
||||
}
|
||||
|
||||
async fn logout(mut auth_session: AuthSession<DummyAuthBackend>) -> Response {
|
||||
async fn logout(mut auth_session: AuthSession<AuthBackend>) -> Response {
|
||||
if auth_session.logout().await.is_err() {
|
||||
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
|
||||
}
|
||||
StatusCode::OK.into_response()
|
||||
}
|
||||
|
||||
async fn logged_in(auth_session: AuthSession<DummyAuthBackend>) -> Response {
|
||||
async fn logged_in(auth_session: AuthSession<AuthBackend>) -> Response {
|
||||
let resp = LoggedInResponse {
|
||||
logged_in: auth_session.user.is_some(),
|
||||
};
|
||||
@ -174,7 +197,14 @@ async fn main() {
|
||||
.with_expiry(Expiry::OnInactivity(Duration::days(1)))
|
||||
.with_signed(signing_key);
|
||||
|
||||
let auth_backend = DummyAuthBackend;
|
||||
let mut users = HashMap::new();
|
||||
users.insert("pjht".to_string(), User {
|
||||
username: "pjht".to_string(),
|
||||
pw_hash: "$argon2id$v=19$m=19456,t=2,p=1$aI7n6SgiTcVhSBjk7pXo+Q$wcJriSwcIj5Al/oNlZJdxMVOA/15e13t2AaWs4VQmVM".to_string(),
|
||||
});
|
||||
let auth_backend = AuthBackend {
|
||||
users
|
||||
};
|
||||
let auth_layer = AuthManagerLayerBuilder::new(auth_backend, session_layer).build();
|
||||
|
||||
let api = Router::new()
|
||||
@ -231,7 +261,7 @@ async fn main() {
|
||||
|
||||
async fn chat_ws(
|
||||
State(state): State<Arc<Mutex<ServState>>>,
|
||||
auth_session: AuthSession<DummyAuthBackend>,
|
||||
auth_session: AuthSession<AuthBackend>,
|
||||
ws: WebSocketUpgrade,
|
||||
) -> Response {
|
||||
ws.on_upgrade(move |socket| async move {
|
||||
|
Loading…
Reference in New Issue
Block a user