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