Make stricter distinction between password and app tokens

This commit is contained in:
Lennart
2025-04-14 18:00:07 +02:00
parent 34b20d4ead
commit 93b967093c
6 changed files with 37 additions and 22 deletions

View File

@@ -70,10 +70,7 @@ pub async fn route_post_login<AP: AuthenticationProvider>(
.and_then(|uri| req.full_url().make_relative(&uri))
.unwrap_or(default_redirect);
if let Ok(Some(user)) = auth_provider
.validate_user_token(&username, &password)
.await
{
if let Ok(Some(user)) = auth_provider.validate_password(&username, &password).await {
session.insert("user", user.id).unwrap();
Redirect::to(redirect_uri)
.see_other()

View File

@@ -70,7 +70,7 @@ where
let user_id = auth.as_ref().user_id();
if let Some(password) = auth.as_ref().password() {
if let Ok(Some(user)) = auth_provider
.validate_user_token(user_id, password)
.validate_app_token(user_id, password)
.instrument(info_span!("validate_user_token"))
.await
{

View File

@@ -8,7 +8,9 @@ use async_trait::async_trait;
pub trait AuthenticationProvider: 'static {
async fn get_principal(&self, id: &str) -> Result<Option<User>, crate::Error>;
async fn insert_principal(&self, user: User) -> Result<(), crate::Error>;
async fn validate_user_token(&self, user_id: &str, token: &str) -> Result<Option<User>, Error>;
async fn validate_password(&self, user_id: &str, password: &str)
-> Result<Option<User>, Error>;
async fn validate_app_token(&self, user_id: &str, token: &str) -> Result<Option<User>, Error>;
/// Returns a token identifier
async fn add_app_token(
&self,

View File

@@ -75,29 +75,37 @@ impl AuthenticationProvider for TomlPrincipalStore {
Ok(())
}
async fn validate_user_token(&self, user_id: &str, token: &str) -> Result<Option<User>, Error> {
async fn validate_password(
&self,
user_id: &str,
password_input: &str,
) -> Result<Option<User>, Error> {
let user: User = match self.get_principal(user_id).await? {
Some(user) => user,
None => return Ok(None),
};
// 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.app_tokens {
if password_auth::verify_password(token, &app_token.token).is_ok() {
return Ok(Some(user));
}
}
let password = match &user.password {
Some(password) => password,
None => return Ok(None),
};
if password_auth::verify_password(token, password).is_ok() {
if password_auth::verify_password(password_input, password).is_ok() {
return Ok(Some(user));
}
Ok(None)
}
async fn validate_app_token(&self, user_id: &str, token: &str) -> Result<Option<User>, Error> {
let user: User = match self.get_principal(user_id).await? {
Some(user) => user,
None => return Ok(None),
};
for app_token in &user.app_tokens {
if password_auth::verify_password(token, &app_token.token).is_ok() {
return Ok(Some(user));
}
}
Ok(None)
}