Prep for a "real" login system and sessions
This commit is contained in:
parent
305c1bd011
commit
a9dd1f9316
238
Cargo.lock
generated
238
Cargo.lock
generated
|
@ -181,6 +181,26 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-login"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fbc0d7bd2577dda9aa9cac096e53b30342725d8eea5798169ff2537a214f45"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
"form_urlencoded",
|
||||
"serde",
|
||||
"subtle",
|
||||
"thiserror",
|
||||
"tower-cookies",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tower-sessions",
|
||||
"tracing",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-macros"
|
||||
version = "0.4.1"
|
||||
|
@ -214,6 +234,12 @@ version = "0.21.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
|
@ -429,6 +455,17 @@ dependencies = [
|
|||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
|
@ -493,6 +530,16 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
|
@ -963,6 +1010,7 @@ checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
|||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -977,6 +1025,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"argon2",
|
||||
"axum",
|
||||
"axum-login",
|
||||
"bincode",
|
||||
"chrono",
|
||||
"clap",
|
||||
|
@ -993,6 +1042,8 @@ dependencies = [
|
|||
"tokio",
|
||||
"tower-http",
|
||||
"tower-livereload",
|
||||
"tower-sessions",
|
||||
"tower-sessions-sled-store",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uuid",
|
||||
|
@ -1124,6 +1175,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
|
@ -1239,6 +1296,12 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.1"
|
||||
|
@ -1328,6 +1391,18 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
|
@ -1369,6 +1444,27 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
|
@ -1458,13 +1554,35 @@ version = "0.8.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
||||
|
||||
[[package]]
|
||||
name = "rmp"
|
||||
version = "0.8.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"num-traits",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmp-serde"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"rmp",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.7",
|
||||
"bitflags 2.5.0",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
@ -1710,6 +1828,37 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
|
@ -1812,6 +1961,23 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-cookies"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fd0118512cf0b3768f7fcccf0bef1ae41d68f2b45edc1e77432b36c97c56c6d"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"cookie",
|
||||
"futures-util",
|
||||
"http",
|
||||
"parking_lot 0.12.2",
|
||||
"pin-project-lite",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.5.2"
|
||||
|
@ -1863,6 +2029,69 @@ version = "0.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
||||
|
||||
[[package]]
|
||||
name = "tower-sessions"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2d9b6f0c4938eed0eefd9cce19319b4bdad10e11ca9d8c3be373ce734bbfd63"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"http",
|
||||
"time",
|
||||
"tokio",
|
||||
"tower-cookies",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tower-sessions-core",
|
||||
"tower-sessions-memory-store",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-sessions-core"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38767064990c327ec1d92bba2576dce0944750e9c9ae021f12ebc72de77ac406"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"base64 0.22.1",
|
||||
"futures",
|
||||
"http",
|
||||
"parking_lot 0.12.2",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-sessions-memory-store"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8b09bbe2c138a9b0ebf307dc6e6a4f7723c59545e0f4fe5e329a89868164ae3"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"time",
|
||||
"tokio",
|
||||
"tower-sessions-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-sessions-sled-store"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/lytedev/tower-sessions-sled-store.git?branch=tower-sessions-0.12#95e9ffd89c971f0199003ed4c3b5cba2e2dba254"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"rmp-serde",
|
||||
"sled",
|
||||
"tokio",
|
||||
"tower-sessions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
|
@ -1968,6 +2197,12 @@ version = "1.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
|
@ -1982,6 +2217,7 @@ checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
|||
dependencies = [
|
||||
"atomic",
|
||||
"getrandom",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -20,6 +20,7 @@ panic = "abort"
|
|||
|
||||
[dependencies]
|
||||
argon2 = { version = "0.5.3", features = ["std"] }
|
||||
axum-login = "0.15.1"
|
||||
bincode = "1.3.3"
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
clap = { version = "4.5.4", features = ["derive", "env"] }
|
||||
|
@ -36,9 +37,11 @@ thiserror = "1.0.60"
|
|||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
tower-http = { version = "0.5.2", features = ["fs", "trace"] }
|
||||
tower-livereload = "0.9.2"
|
||||
tower-sessions = "0.12.2"
|
||||
tower-sessions-sled-store = { git = "https://github.com/lytedev/tower-sessions-sled-store.git", branch = "tower-sessions-0.12" }
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
uuid = { version = "1.8.0", features = ["v7"] }
|
||||
uuid = { version = "1.8.0", features = ["v7", "serde"] }
|
||||
walkdir = "2.5.0"
|
||||
|
||||
[dependencies.axum]
|
||||
|
|
|
@ -14,6 +14,7 @@ use axum::{
|
|||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use axum_login::login_required;
|
||||
use maud::html;
|
||||
use sled::IVec;
|
||||
use thiserror::Error;
|
||||
|
@ -80,6 +81,8 @@ pub async fn router(
|
|||
.route("/", get(index))
|
||||
.route("/about", get(about))
|
||||
.route("/users", get(users))
|
||||
.route("/dashboard", get(dashboard))
|
||||
.route_layer(login_required!(AppState, login_url = "/auth/login"))
|
||||
.nest_service("/auth", auth_service)
|
||||
.nest_service("/static", static_file_service)
|
||||
.layer(TraceLayer::new_for_http())
|
||||
|
@ -99,7 +102,11 @@ async fn index() -> ReqResult<Html<String>> {
|
|||
}
|
||||
|
||||
async fn about() -> ReqResult<Html<String>> {
|
||||
page("index", html! { "About" })
|
||||
page("about", html! { "About" })
|
||||
}
|
||||
|
||||
async fn dashboard() -> ReqResult<Html<String>> {
|
||||
page("dashboard", html! { "Dashboard" })
|
||||
}
|
||||
|
||||
async fn users(State(state): State<AppState>) -> ReqResult<String> {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::router::ReqResult;
|
||||
use crate::state::State as AppState;
|
||||
use crate::state::{Creds, State as AppState};
|
||||
use crate::{partials::*, user::User};
|
||||
use axum::extract::State;
|
||||
use axum::response::Html;
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{Html, IntoResponse, Redirect};
|
||||
use axum::Form;
|
||||
use maud::html;
|
||||
use redact::Secret;
|
||||
|
@ -63,40 +64,24 @@ async fn register() -> ReqResult<Html<String>> {
|
|||
page("login", center_hero_form("Register", form, subaction))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct Creds {
|
||||
username: String,
|
||||
password: Secret<String>,
|
||||
}
|
||||
type AuthSession = axum_login::AuthSession<AppState>;
|
||||
|
||||
#[instrument(skip(state))]
|
||||
#[instrument(skip(auth_session))]
|
||||
async fn authenticate(
|
||||
State(state): State<AppState>,
|
||||
mut auth_session: AuthSession,
|
||||
Form(creds): Form<Creds>,
|
||||
) -> ReqResult<Html<String>> {
|
||||
let existing_user: Option<User> = state.db.get(User::tree(), &creds.username)?;
|
||||
) -> impl IntoResponse {
|
||||
let user = match auth_session.authenticate(creds.clone()).await {
|
||||
Ok(Some(user)) => user,
|
||||
Ok(None) => return StatusCode::UNAUTHORIZED.into_response(),
|
||||
Err(_) => return StatusCode::INTERNAL_SERVER_ERROR.into_response(),
|
||||
};
|
||||
|
||||
if existing_user.is_none() {
|
||||
// timing/enumeration attacks or something
|
||||
return Err(user::Error::UsernameNotFound(Box::new(creds.username)).into());
|
||||
if auth_session.login(&user).await.is_err() {
|
||||
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
|
||||
}
|
||||
|
||||
let existing_user = existing_user.unwrap();
|
||||
if let Err(err) = existing_user.verify(creds.password.expose_secret()) {
|
||||
Ok(Html(
|
||||
html! {
|
||||
"failed to login: " ({err})
|
||||
}
|
||||
.into_string(),
|
||||
))
|
||||
} else {
|
||||
Ok(Html(
|
||||
html! {
|
||||
"logged in"
|
||||
}
|
||||
.into_string(),
|
||||
))
|
||||
}
|
||||
Redirect::to("/dashboard").into_response()
|
||||
}
|
||||
|
||||
#[instrument(skip(state))]
|
||||
|
|
52
src/state.rs
52
src/state.rs
|
@ -1,7 +1,12 @@
|
|||
use crate::{
|
||||
db::{self, Data},
|
||||
prelude::*,
|
||||
user::{self, User},
|
||||
};
|
||||
use axum::async_trait;
|
||||
use axum_login::{AuthnBackend, UserId};
|
||||
use redact::Secret;
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -22,3 +27,50 @@ pub enum NewStateError {
|
|||
#[error("database error: {0}")]
|
||||
Database(#[from] db::Error),
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct Creds {
|
||||
pub username: String,
|
||||
pub password: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AuthError {
|
||||
#[error("user error: {0}")]
|
||||
User(#[from] user::Error),
|
||||
|
||||
#[error("data error: {0}")]
|
||||
Db(#[from] db::Error),
|
||||
|
||||
#[error("data error: {0}")]
|
||||
Argon2(#[from] argon2::password_hash::Error),
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AuthnBackend for State {
|
||||
type User = User;
|
||||
type Credentials = Creds;
|
||||
type Error = AuthError;
|
||||
|
||||
async fn authenticate(
|
||||
&self,
|
||||
Creds { username, password }: Self::Credentials,
|
||||
) -> Result<Option<Self::User>, Self::Error> {
|
||||
if let Some(user) = self.db.get::<String, User>(User::tree(), username)? {
|
||||
if let Err(err) = user.verify(password.expose_secret()) {
|
||||
Err(err.into())
|
||||
} else {
|
||||
Ok(Some(user))
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_user(&self, username: &UserId<Self>) -> Result<Option<Self::User>, Self::Error> {
|
||||
match self.db.get::<&str, User>(User::tree(), username)? {
|
||||
Some(user) => Ok(Some(user)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
src/user.rs
28
src/user.rs
|
@ -1,17 +1,20 @@
|
|||
use crate::prelude::*;
|
||||
use axum_login::AuthUser;
|
||||
use chrono::Utc;
|
||||
use redact::{expose_secret, Secret};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use uuid::{NoContext, Uuid};
|
||||
|
||||
pub const USER_TREE: &str = "user";
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct User {
|
||||
pub id: Uuid,
|
||||
pub username: String,
|
||||
|
||||
#[serde(serialize_with = "expose_secret")]
|
||||
password_digest: Secret<Vec<u8>>,
|
||||
password_digest: Secret<String>,
|
||||
|
||||
pub registered_at: chrono::DateTime<Utc>,
|
||||
}
|
||||
|
@ -31,9 +34,16 @@ impl User {
|
|||
}
|
||||
|
||||
pub fn try_new(username: &str, password: &str) -> Result<Self, argon2::password_hash::Error> {
|
||||
let now = Utc::now();
|
||||
let ts = uuid::Timestamp::from_unix(
|
||||
NoContext,
|
||||
u64::from_ne_bytes(now.timestamp().to_ne_bytes()),
|
||||
now.timestamp_subsec_micros(),
|
||||
);
|
||||
Ok(Self {
|
||||
id: Uuid::new_v7(ts),
|
||||
username: username.to_owned(),
|
||||
registered_at: Utc::now(),
|
||||
registered_at: now,
|
||||
password_digest: Secret::new(crate::auth::password_digest(password)?.into()),
|
||||
})
|
||||
}
|
||||
|
@ -49,3 +59,15 @@ impl TryFrom<sled::IVec> for User {
|
|||
bincode::deserialize(&value)
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthUser for User {
|
||||
type Id = String;
|
||||
|
||||
fn id(&self) -> Self::Id {
|
||||
self.username.clone()
|
||||
}
|
||||
|
||||
fn session_auth_hash(&self) -> &[u8] {
|
||||
self.password_digest.expose_secret().as_bytes()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue