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
59pub struct AuthConfigurator {
61 session_ttl: Option<Duration>,
62 challenge_ttl: Duration,
63}
64
65impl Default for AuthConfigurator {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl AuthConfigurator {
72 pub fn new() -> Self {
73 Self {
74 session_ttl: Some(Duration::from_secs(24 * 60 * 60)), challenge_ttl: Duration::from_secs(60),
76 }
77 }
78
79 pub fn session_ttl(mut self, ttl: Duration) -> Self {
80 self.session_ttl = Some(ttl);
81 self
82 }
83
84 pub fn no_session_ttl(mut self) -> Self {
85 self.session_ttl = None;
86 self
87 }
88
89 pub fn challenge_ttl(mut self, ttl: Duration) -> Self {
90 self.challenge_ttl = ttl;
91 self
92 }
93
94 pub fn configure(self) -> AuthServiceConfig {
95 AuthServiceConfig {
96 session_ttl: self.session_ttl,
97 challenge_ttl: self.challenge_ttl,
98 }
99 }
100}
101
102#[derive(Debug, Clone)]
104pub struct AuthServiceConfig {
105 pub session_ttl: Option<Duration>,
107 pub challenge_ttl: Duration,
109}
110
111impl Default for AuthServiceConfig {
112 fn default() -> Self {
113 AuthConfigurator::new().configure()
114 }
115}
116
117pub struct Inner {
118 pub(crate) engine: Arc<dyn AuthEngine>,
119 pub(crate) auth_registry: Arc<AuthenticationRegistry>,
120 pub(crate) challenges: ChallengeStore,
121 pub(crate) rng: SystemRng,
122 pub(crate) clock: Clock,
123 pub(crate) session_ttl: Option<Duration>,
124}
125
126#[derive(Clone)]
134pub struct AuthService(Arc<Inner>);
135
136impl Deref for AuthService {
137 type Target = Inner;
138 fn deref(&self) -> &Inner {
139 &self.0
140 }
141}
142
143impl AuthService {
144 pub fn new(
145 engine: Arc<dyn AuthEngine>,
146 auth_registry: Arc<AuthenticationRegistry>,
147 rng: SystemRng,
148 clock: Clock,
149 config: AuthServiceConfig,
150 ) -> Self {
151 Self(Arc::new(Inner {
152 engine,
153 auth_registry,
154 challenges: ChallengeStore::new(config.challenge_ttl),
155 rng,
156 clock,
157 session_ttl: config.session_ttl,
158 }))
159 }
160
161 pub(super) fn now(&self) -> Result<DateTime, Error> {
163 Ok(DateTime::from_nanos(self.clock.now_nanos()))
164 }
165
166 pub(super) fn expires_at(&self) -> Result<Option<DateTime>, Error> {
168 match self.session_ttl {
169 Some(ttl) => {
170 let ttl_nanos = ttl.as_nanos() as u64;
171 let nanos = self.clock.now_nanos().saturating_add(ttl_nanos);
172 Ok(Some(DateTime::from_nanos(nanos)))
173 }
174 None => Ok(None),
175 }
176 }
177
178 pub(super) fn persist_token(&self, token: &str, identity: IdentityId) -> Result<Token, Error> {
180 let mut admin = self.engine.begin_admin()?;
181
182 let def = create_token(&mut admin, token, identity, self.expires_at()?, self.now()?)?;
183
184 admin.commit()?;
185 Ok(def)
186 }
187
188 pub fn create_token(
195 &self,
196 token: &str,
197 identity: IdentityId,
198 expires_at: Option<DateTime>,
199 ) -> Result<Token, Error> {
200 let mut admin = self.engine.begin_admin()?;
201 let def = create_token(&mut admin, token, identity, expires_at, self.now()?)?;
202 admin.commit()?;
203 Ok(def)
204 }
205}
206
207pub(super) fn generate_session_token(rng: &SystemRng) -> String {
212 let bytes = rng.infra_bytes_32();
213 bytes.iter().map(|b| format!("{:02x}", b)).collect()
214}