Add app tokens (secondary passwords) to afford cheaper hashes

This commit is contained in:
Lennart
2024-10-06 12:54:19 +02:00
parent a119e7f099
commit f7d253de85
4 changed files with 50 additions and 9 deletions

View File

@@ -7,18 +7,31 @@ use super::AuthenticationProvider;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct StaticUserStoreConfig {
users: Vec<User>,
users: Vec<UserEntry>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UserEntry {
#[serde(flatten)]
pub user: User,
#[serde(default)]
pub app_tokens: Vec<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct StaticUserStore {
pub users: HashMap<String, User>,
pub users: HashMap<String, UserEntry>,
}
impl StaticUserStore {
pub fn new(config: StaticUserStoreConfig) -> Self {
Self {
users: HashMap::from_iter(config.users.into_iter().map(|user| (user.id.clone(), user))),
users: HashMap::from_iter(
config
.users
.into_iter()
.map(|user_entry| (user_entry.user.id.clone(), user_entry)),
),
}
}
}
@@ -26,18 +39,27 @@ impl StaticUserStore {
#[async_trait]
impl AuthenticationProvider for StaticUserStore {
async fn validate_user_token(&self, user_id: &str, token: &str) -> Result<Option<User>, Error> {
let user: User = match self.users.get(user_id) {
let user_entry: UserEntry = match self.users.get(user_id) {
Some(user) => user.clone(),
None => return Ok(None),
};
let password = match &user.password {
let password = match &user_entry.user.password {
Some(password) => password,
None => return Ok(None),
};
Ok(password_auth::verify_password(token, password)
.map(|()| user)
.ok())
// Try app tokens first since they are cheaper to calculate
// They can afford less iterations since they can be generated with high entropy
for app_token in &user_entry.app_tokens {
if password_auth::verify_password(token, app_token).is_ok() {
return Ok(Some(user_entry.user));
}
}
if password_auth::verify_password(token, password).is_ok() {
return Ok(Some(user_entry.user));
}
Ok(None)
}
}