Skip to main content

reifydb_auth/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_value::{
5	error::{Diagnostic, Error, IntoDiagnostic},
6	fragment::Fragment,
7};
8
9pub type Result<T> = std::result::Result<T, Error>;
10
11#[derive(Debug, thiserror::Error)]
12pub enum AuthError {
13	#[error("password is required for password authentication")]
14	PasswordRequired,
15
16	#[error("stored authentication is missing hash")]
17	MissingHash,
18
19	#[error("stored authentication is missing salt")]
20	MissingSalt,
21
22	#[error("stored authentication is missing token")]
23	MissingToken,
24
25	#[error("unknown authentication method: {method}")]
26	UnknownMethod {
27		method: String,
28	},
29
30	#[error("failed to serialize authentication properties: {reason}")]
31	SerializeProperties {
32		reason: String,
33	},
34
35	#[error("password hashing failed: {reason}")]
36	HashingFailed {
37		reason: String,
38	},
39
40	#[error("stored hash is invalid or corrupted: {reason}")]
41	InvalidHash {
42		reason: String,
43	},
44
45	#[error("password verification failed: {reason}")]
46	VerificationFailed {
47		reason: String,
48	},
49}
50
51#[derive(Debug, thiserror::Error)]
52pub enum SolanaError {
53	#[error("public key is required for solana authentication")]
54	MissingPublicKey,
55
56	#[error("invalid public key: {reason}")]
57	InvalidPublicKey {
58		reason: String,
59	},
60
61	#[error("invalid signature: {reason}")]
62	InvalidSignature {
63		reason: String,
64	},
65}
66
67#[derive(Debug, thiserror::Error)]
68pub enum GithubError {
69	#[error("user_id is required for github authentication")]
70	MissingUserId,
71
72	#[error("invalid github user_id: {reason}")]
73	InvalidUserId {
74		reason: String,
75	},
76
77	#[error("github oauth code exchange failed: {reason}")]
78	ExchangeFailed {
79		reason: String,
80	},
81
82	#[error("github api request failed: {reason}")]
83	ApiFailed {
84		reason: String,
85	},
86}
87
88impl IntoDiagnostic for AuthError {
89	fn into_diagnostic(self) -> Diagnostic {
90		match self {
91			AuthError::PasswordRequired => Diagnostic {
92				code: "AU_001".to_string(),
93				rql: None,
94				message: "password is required for password authentication".to_string(),
95				fragment: Fragment::None,
96				label: Some("missing password".to_string()),
97				help: Some("provide a password in the authentication configuration".to_string()),
98				column: None,
99				notes: vec![],
100				cause: None,
101				operator_chain: None,
102			},
103
104			AuthError::MissingHash => Diagnostic {
105				code: "AU_002".to_string(),
106				rql: None,
107				message: "stored authentication is missing hash".to_string(),
108				fragment: Fragment::None,
109				label: Some("missing hash".to_string()),
110				help: Some("the stored authentication record is corrupted or incomplete".to_string()),
111				column: None,
112				notes: vec![],
113				cause: None,
114				operator_chain: None,
115			},
116
117			AuthError::MissingSalt => Diagnostic {
118				code: "AU_003".to_string(),
119				rql: None,
120				message: "stored authentication is missing salt".to_string(),
121				fragment: Fragment::None,
122				label: Some("missing salt".to_string()),
123				help: Some("the stored authentication record is corrupted or incomplete".to_string()),
124				column: None,
125				notes: vec![],
126				cause: None,
127				operator_chain: None,
128			},
129
130			AuthError::MissingToken => Diagnostic {
131				code: "AU_004".to_string(),
132				rql: None,
133				message: "stored authentication is missing token".to_string(),
134				fragment: Fragment::None,
135				label: Some("missing token".to_string()),
136				help: Some("the stored authentication record is corrupted or incomplete".to_string()),
137				column: None,
138				notes: vec![],
139				cause: None,
140				operator_chain: None,
141			},
142
143			AuthError::SerializeProperties {
144				reason,
145			} => Diagnostic {
146				code: "AU_006".to_string(),
147				rql: None,
148				message: format!("failed to serialize authentication properties: {}", reason),
149				fragment: Fragment::None,
150				label: Some("serialization failed".to_string()),
151				help: Some("ensure authentication properties are valid".to_string()),
152				column: None,
153				notes: vec![],
154				cause: None,
155				operator_chain: None,
156			},
157
158			AuthError::UnknownMethod {
159				method,
160			} => Diagnostic {
161				code: "AU_005".to_string(),
162				rql: None,
163				message: format!("unknown authentication method: {}", method),
164				fragment: Fragment::None,
165				label: Some("unknown method".to_string()),
166				help: Some("supported authentication methods are: password, token, solana, github"
167					.to_string()),
168				column: None,
169				notes: vec![],
170				cause: None,
171				operator_chain: None,
172			},
173
174			AuthError::HashingFailed {
175				reason,
176			} => Diagnostic {
177				code: "AU_007".to_string(),
178				rql: None,
179				message: format!("password hashing failed: {}", reason),
180				fragment: Fragment::None,
181				label: Some("hashing failed".to_string()),
182				help: Some("an internal error occurred during password hashing".to_string()),
183				column: None,
184				notes: vec![],
185				cause: None,
186				operator_chain: None,
187			},
188
189			AuthError::InvalidHash {
190				reason,
191			} => Diagnostic {
192				code: "AU_008".to_string(),
193				rql: None,
194				message: format!("stored hash is invalid or corrupted: {}", reason),
195				fragment: Fragment::None,
196				label: Some("invalid hash".to_string()),
197				help: Some("the stored authentication record is corrupted or incomplete".to_string()),
198				column: None,
199				notes: vec![],
200				cause: None,
201				operator_chain: None,
202			},
203
204			AuthError::VerificationFailed {
205				reason,
206			} => Diagnostic {
207				code: "AU_009".to_string(),
208				rql: None,
209				message: format!("password verification failed: {}", reason),
210				fragment: Fragment::None,
211				label: Some("verification failed".to_string()),
212				help: Some("an internal error occurred during password verification".to_string()),
213				column: None,
214				notes: vec![],
215				cause: None,
216				operator_chain: None,
217			},
218		}
219	}
220}
221
222impl From<AuthError> for Error {
223	fn from(err: AuthError) -> Self {
224		Error(Box::new(err.into_diagnostic()))
225	}
226}
227
228impl IntoDiagnostic for SolanaError {
229	fn into_diagnostic(self) -> Diagnostic {
230		match self {
231			SolanaError::MissingPublicKey => Diagnostic {
232				code: "AU_010".to_string(),
233				rql: None,
234				message: "public key is required for solana authentication".to_string(),
235				fragment: Fragment::None,
236				label: Some("missing public key".to_string()),
237				help: Some("provide a public_key in the authentication configuration".to_string()),
238				column: None,
239				notes: vec![],
240				cause: None,
241				operator_chain: None,
242			},
243
244			SolanaError::InvalidPublicKey {
245				reason,
246			} => Diagnostic {
247				code: "AU_011".to_string(),
248				rql: None,
249				message: format!("invalid public key: {}", reason),
250				fragment: Fragment::None,
251				label: Some("invalid public key".to_string()),
252				help: Some("provide a valid base58-encoded 32-byte Solana public key".to_string()),
253				column: None,
254				notes: vec![],
255				cause: None,
256				operator_chain: None,
257			},
258
259			SolanaError::InvalidSignature {
260				reason,
261			} => Diagnostic {
262				code: "AU_012".to_string(),
263				rql: None,
264				message: format!("invalid signature: {}", reason),
265				fragment: Fragment::None,
266				label: Some("invalid signature".to_string()),
267				help: Some("provide a valid base58-encoded 64-byte ed25519 signature".to_string()),
268				column: None,
269				notes: vec![],
270				cause: None,
271				operator_chain: None,
272			},
273		}
274	}
275}
276
277impl From<SolanaError> for Error {
278	fn from(err: SolanaError) -> Self {
279		Error(Box::new(err.into_diagnostic()))
280	}
281}
282
283impl IntoDiagnostic for GithubError {
284	fn into_diagnostic(self) -> Diagnostic {
285		match self {
286			GithubError::MissingUserId => Diagnostic {
287				code: "AU_013".to_string(),
288				rql: None,
289				message: "user_id is required for github authentication".to_string(),
290				fragment: Fragment::None,
291				label: Some("missing user_id".to_string()),
292				help: Some("provide a user_id in the authentication configuration".to_string()),
293				column: None,
294				notes: vec![],
295				cause: None,
296				operator_chain: None,
297			},
298
299			GithubError::InvalidUserId {
300				reason,
301			} => Diagnostic {
302				code: "AU_014".to_string(),
303				rql: None,
304				message: format!("invalid github user_id: {}", reason),
305				fragment: Fragment::None,
306				label: Some("invalid user_id".to_string()),
307				help: Some("provide the numeric github account id as user_id".to_string()),
308				column: None,
309				notes: vec![],
310				cause: None,
311				operator_chain: None,
312			},
313
314			GithubError::ExchangeFailed {
315				reason,
316			} => Diagnostic {
317				code: "AU_015".to_string(),
318				rql: None,
319				message: format!("github oauth code exchange failed: {}", reason),
320				fragment: Fragment::None,
321				label: Some("code exchange failed".to_string()),
322				help: Some("verify the github oauth client id, client secret, and redirect uri"
323					.to_string()),
324				column: None,
325				notes: vec![],
326				cause: None,
327				operator_chain: None,
328			},
329
330			GithubError::ApiFailed {
331				reason,
332			} => Diagnostic {
333				code: "AU_016".to_string(),
334				rql: None,
335				message: format!("github api request failed: {}", reason),
336				fragment: Fragment::None,
337				label: Some("github api failed".to_string()),
338				help: Some("github may be unreachable; retry the sign-in".to_string()),
339				column: None,
340				notes: vec![],
341				cause: None,
342				operator_chain: None,
343			},
344		}
345	}
346}
347
348impl From<GithubError> for Error {
349	fn from(err: GithubError) -> Self {
350		Error(Box::new(err.into_diagnostic()))
351	}
352}