example registration and login with token
cargo devel CI / cargo CI (push) Failing after 1m48s Details

This commit is contained in:
Christoph J. Scherr 2024-06-24 13:45:30 +02:00
parent 32f9bc2ab7
commit 8173a8da7a
4 changed files with 188 additions and 0 deletions

53
Cargo.lock generated
View File

@ -101,6 +101,18 @@ version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
[[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-stream"
version = "0.3.5"
@ -176,6 +188,12 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "binascii"
version = "0.1.4"
@ -194,6 +212,15 @@ version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[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"
@ -626,6 +653,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
@ -1534,6 +1562,14 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "onlytoken"
version = "0.1.0"
dependencies = [
"argon2",
"rand",
]
[[package]]
name = "oorandom"
version = "11.1.3"
@ -1573,6 +1609,17 @@ dependencies = [
"windows-targets 0.48.5",
]
[[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 = "pear"
version = "0.2.8"
@ -2339,6 +2386,12 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "1.0.109"

View File

@ -22,9 +22,11 @@ members = [
"members/rest-queued",
"members/fluent-demo",
"members/fluent-webserv",
"members/onlytoken",
]
default-members = [
".",
"members/onlytoken",
"members/fluent-webserv",
"members/fluent-demo",
"members/echargs",

View File

@ -0,0 +1,10 @@
[package]
name = "onlytoken"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
argon2 = "0.5.3"
rand = "0.8.5"

View File

@ -0,0 +1,123 @@
use argon2::{
password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
Argon2,
};
use rand::seq::SliceRandom;
use std::{collections::HashMap, hash::Hash};
pub const ALPHABET: &str = "qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM";
pub const TOK_LEN: usize = 40;
pub type HashedToken = String;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Token {
inner: String,
}
impl Token {
pub fn new() -> Self {
let mut rng = rand::thread_rng();
let mut data = ALPHABET.to_string().into_bytes();
data.repeat(TOK_LEN);
data.shuffle(&mut rng);
Self {
inner: String::from_utf8(data[..TOK_LEN].to_vec()).unwrap(),
}
}
#[must_use]
pub fn len(&self) -> usize {
self.inner.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn hash(&self, salt: &SaltString) -> HashedToken {
let argon2 = Argon2::default();
let hash = argon2
.hash_password(self.as_bytes(), salt)
.expect("could not hash")
.to_string();
println!("hashed token: {hash}");
hash
}
fn as_bytes(&self) -> &[u8] {
self.inner.as_bytes()
}
}
impl Default for Token {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct User {
data: String,
hashed_tok: String,
}
impl User {
fn new(data: String, salt: &SaltString) -> (Self, Token, HashedToken) {
let token = Token::new();
let hashed_tok = token.hash(salt);
(
User {
data,
hashed_tok: hashed_tok.clone(),
},
token,
hashed_tok,
)
}
}
pub struct Store {
users: HashMap<HashedToken, User>,
salt: SaltString,
}
impl Store {
fn new() -> Self {
let salt = SaltString::generate(&mut OsRng);
Self {
salt,
users: HashMap::new(),
}
}
fn register(&mut self) -> Token {
let (user, token, hashed_tok) = User::new("garbage data".into(), &self.salt);
self.users.insert(hashed_tok, user);
token
}
fn login(&self, tok: Token) -> Option<&User> {
self.users.get(&tok.hash(&self.salt))
}
}
fn main() {
// create the user store
// In a real application, this would be deserialized from a file or lazy-loaded from a database
let mut store = Store::new();
for _ in 0..4 {
// register a few users as noise
let _ = store.register();
}
// create our user and keep the token for that user this time
let tok = store.register();
println!("token of our user is: {tok:?}");
// try to log in with our user
let logged_in = store.login(tok.clone());
// did it work?
assert!(logged_in.is_some());
dbg!(logged_in.unwrap());
}