reifydb_auth/service/
mod.rs1mod authenticate;
11mod solana;
12mod token;
13
14use std::{collections::HashMap, ops::Deref, sync::Arc, time::Duration};
15
16use reifydb_catalog::{catalog::Catalog, create_token};
17use reifydb_core::interface::catalog::token::Token;
18use reifydb_runtime::context::{clock::Clock, rng::Rng as SystemRng};
19use reifydb_transaction::transaction::{admin::AdminTransaction, query::QueryTransaction};
20use reifydb_type::{
21 error::Error,
22 value::{datetime::DateTime, identity::IdentityId},
23};
24
25use crate::{challenge::ChallengeStore, registry::AuthenticationRegistry};
26
27pub trait AuthEngine: Send + Sync {
35 fn begin_admin(&self) -> Result<AdminTransaction, Error>;
36 fn begin_query(&self) -> Result<QueryTransaction, Error>;
37 fn catalog(&self) -> Catalog;
38}
39
40#[derive(Debug, Clone)]
42pub enum AuthResponse {
43 Authenticated {
45 identity: IdentityId,
46 token: String,
47 },
48 Challenge {
50 challenge_id: String,
51 payload: HashMap<String, String>,
52 },
53 Failed {
55 reason: String,
56 },
57}
58
59#[derive(Debug, Clone)]
61pub struct AuthServiceConfig {
62 pub session_ttl: Option<Duration>,
64 pub challenge_ttl: Duration,
66}
67
68impl Default for AuthServiceConfig {
69 fn default() -> Self {
70 Self {
71 session_ttl: Some(Duration::from_secs(24 * 60 * 60)), challenge_ttl: Duration::from_secs(60),
73 }
74 }
75}
76
77impl AuthServiceConfig {
78 pub fn session_ttl(mut self, ttl: Duration) -> Self {
79 self.session_ttl = Some(ttl);
80 self
81 }
82
83 pub fn no_session_ttl(mut self) -> Self {
84 self.session_ttl = None;
85 self
86 }
87
88 pub fn challenge_ttl(mut self, ttl: Duration) -> Self {
89 self.challenge_ttl = ttl;
90 self
91 }
92}
93
94pub struct Inner {
95 pub(crate) engine: Arc<dyn AuthEngine>,
96 pub(crate) auth_registry: Arc<AuthenticationRegistry>,
97 pub(crate) challenges: ChallengeStore,
98 pub(crate) rng: SystemRng,
99 pub(crate) clock: Clock,
100 pub(crate) session_ttl: Option<Duration>,
101}
102
103#[derive(Clone)]
111pub struct AuthService(Arc<Inner>);
112
113impl Deref for AuthService {
114 type Target = Inner;
115 fn deref(&self) -> &Inner {
116 &self.0
117 }
118}
119
120impl AuthService {
121 pub fn new(
122 engine: Arc<dyn AuthEngine>,
123 auth_registry: Arc<AuthenticationRegistry>,
124 rng: SystemRng,
125 clock: Clock,
126 config: AuthServiceConfig,
127 ) -> Self {
128 Self(Arc::new(Inner {
129 engine,
130 auth_registry,
131 challenges: ChallengeStore::new(config.challenge_ttl),
132 rng,
133 clock,
134 session_ttl: config.session_ttl,
135 }))
136 }
137
138 pub(super) fn now(&self) -> Result<DateTime, Error> {
140 Ok(DateTime::from_timestamp_nanos(self.clock.now_nanos())?)
141 }
142
143 pub(super) fn expires_at(&self) -> Result<Option<DateTime>, Error> {
145 match self.session_ttl {
146 Some(ttl) => {
147 let nanos = self.clock.now_nanos() + ttl.as_nanos();
148 Ok(Some(DateTime::from_timestamp_nanos(nanos)?))
149 }
150 None => Ok(None),
151 }
152 }
153
154 pub(super) fn persist_token(&self, token: &str, identity: IdentityId) -> Result<Token, Error> {
156 let mut admin = self.engine.begin_admin()?;
157
158 let def = create_token(&mut admin, token, identity, self.expires_at()?, self.now()?)?;
159
160 admin.commit()?;
161 Ok(def)
162 }
163}
164
165pub(super) fn generate_session_token(rng: &SystemRng) -> String {
167 let bytes = rng.bytes_32();
168 bytes.iter().map(|b| format!("{:02x}", b)).collect()
169}