summaryrefslogtreecommitdiff
path: root/src/login/repo/tokens.rs
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-09-03 01:25:20 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-09-03 02:09:25 -0400
commitb404344a7c4ab5cb6c7d7b445fab796be79b848f (patch)
treec476b125316b9d4aa7bdece7c9bb8e2f65d2961e /src/login/repo/tokens.rs
parent92a7518975c6bc4b2f9b9c6c12c458b24e8cfaf5 (diff)
Allow login creation and authentication.
This is a beefy change, as it adds a TON of smaller pieces needed to make this all function: * A database migration. * A ton of new crates for things like password validation, timekeeping, and HTML generation. * A first cut at a module structure for routes, templates, repositories. * A family of ID types, for identifying various kinds of domain thing. * AppError, which _doesn't_ implement Error but can be sent to clients.
Diffstat (limited to 'src/login/repo/tokens.rs')
-rw-r--r--src/login/repo/tokens.rs47
1 files changed, 47 insertions, 0 deletions
diff --git a/src/login/repo/tokens.rs b/src/login/repo/tokens.rs
new file mode 100644
index 0000000..080e35a
--- /dev/null
+++ b/src/login/repo/tokens.rs
@@ -0,0 +1,47 @@
+use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction};
+use uuid::Uuid;
+
+use super::logins::Id as LoginId;
+use crate::error::BoxedError;
+
+type DateTime = chrono::DateTime<chrono::Utc>;
+
+pub trait Provider {
+ fn tokens(&mut self) -> Tokens;
+}
+
+impl<'c> Provider for Transaction<'c, Sqlite> {
+ fn tokens(&mut self) -> Tokens {
+ Tokens(self)
+ }
+}
+
+pub struct Tokens<'t>(&'t mut SqliteConnection);
+
+impl<'c> Tokens<'c> {
+ /// Issue a new token for an existing login. The issued_at timestamp will
+ /// be used to control expiry.
+ pub async fn issue(
+ &mut self,
+ login: &LoginId,
+ issued_at: DateTime,
+ ) -> Result<String, BoxedError> {
+ let secret = Uuid::new_v4().to_string();
+
+ let secret = sqlx::query_scalar!(
+ r#"
+ insert
+ into token (secret, login, issued_at)
+ values ($1, $2, $3)
+ returning secret as "secret!"
+ "#,
+ secret,
+ login,
+ issued_at,
+ )
+ .fetch_one(&mut *self.0)
+ .await?;
+
+ Ok(secret)
+ }
+}