summaryrefslogtreecommitdiff
path: root/src/login/app.rs
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-09-28 01:40:22 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-09-28 20:48:40 -0400
commit4d0bb0709b168a24ab6a8dbc86da45d7503596ee (patch)
tree031f2e35f07cef7305809e3a1d310bf304d15460 /src/login/app.rs
parent72efedf8e96ca6e159ce6146809ee6d3a9e5a0e7 (diff)
Wrap credential and credential-holding types to prevent `Debug` leaks.
The following values are considered confidential, and should never be logged, even by accident: * `Password`, which is a durable bearer token for a specific Login; * `IdentitySecret`, which is an ephemeral but potentially long-lived bearer token for a specific Login; or * `IdentityToken`, which may hold cookies containing an `IdentitySecret`. These values are now wrapped in types whose `Debug` impls output opaque values, so that they can be included in structs that `#[derive(Debug)]` without requiring any additional care. The wrappers also avoid implementing `Display`, to prevent inadvertent `to_string()`s. We don't bother obfuscating `IdentitySecret`s in memory or in the `.hi` database. There's no point: we'd also need to store the information needed to de-obfuscate them, and they can be freely invalidated and replaced by blanking that table and asking everyone to log in again. Passwords _are_ obfuscated for storage, as they're intended to be durable.
Diffstat (limited to 'src/login/app.rs')
-rw-r--r--src/login/app.rs22
1 files changed, 13 insertions, 9 deletions
diff --git a/src/login/app.rs b/src/login/app.rs
index 292b95f..f7fec88 100644
--- a/src/login/app.rs
+++ b/src/login/app.rs
@@ -1,10 +1,10 @@
use chrono::TimeDelta;
use sqlx::sqlite::SqlitePool;
-use super::repo::auth::Provider as _;
+use super::{extract::IdentitySecret, repo::auth::Provider as _};
use crate::{
clock::DateTime,
- password::StoredHash,
+ password::Password,
repo::{
error::NotFound as _,
login::{Login, Provider as _},
@@ -24,9 +24,9 @@ impl<'a> Logins<'a> {
pub async fn login(
&self,
name: &str,
- password: &str,
+ password: &Password,
login_at: &DateTime,
- ) -> Result<String, LoginError> {
+ ) -> Result<IdentitySecret, LoginError> {
let mut tx = self.db.begin().await?;
let login = if let Some((login, stored_hash)) = tx.auth().for_name(name).await? {
@@ -38,7 +38,7 @@ impl<'a> Logins<'a> {
return Err(LoginError::Rejected);
}
} else {
- let password_hash = StoredHash::new(password)?;
+ let password_hash = password.hash()?;
tx.logins().create(name, &password_hash).await?
};
@@ -49,8 +49,8 @@ impl<'a> Logins<'a> {
}
#[cfg(test)]
- pub async fn create(&self, name: &str, password: &str) -> Result<Login, CreateError> {
- let password_hash = StoredHash::new(password)?;
+ pub async fn create(&self, name: &str, password: &Password) -> Result<Login, CreateError> {
+ let password_hash = password.hash()?;
let mut tx = self.db.begin().await?;
let login = tx.logins().create(name, &password_hash).await?;
@@ -59,7 +59,11 @@ impl<'a> Logins<'a> {
Ok(login)
}
- pub async fn validate(&self, secret: &str, used_at: &DateTime) -> Result<Login, ValidateError> {
+ pub async fn validate(
+ &self,
+ secret: &IdentitySecret,
+ used_at: &DateTime,
+ ) -> Result<Login, ValidateError> {
let mut tx = self.db.begin().await?;
let login = tx
.tokens()
@@ -82,7 +86,7 @@ impl<'a> Logins<'a> {
Ok(())
}
- pub async fn logout(&self, secret: &str) -> Result<(), ValidateError> {
+ pub async fn logout(&self, secret: &IdentitySecret) -> Result<(), ValidateError> {
let mut tx = self.db.begin().await?;
tx.tokens()
.revoke(secret)