Skip to main content

reifydb_sub_server/
auth.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::{error::Error as StdError, fmt};
5
6use reifydb_auth::service::AuthService;
7use reifydb_value::value::identity::IdentityId;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum AuthError {
11	InvalidHeader,
12
13	MissingCredentials,
14
15	InvalidToken,
16
17	Expired,
18
19	InsufficientPermissions,
20}
21
22impl fmt::Display for AuthError {
23	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24		match self {
25			AuthError::InvalidHeader => write!(f, "Invalid authorization header"),
26			AuthError::MissingCredentials => write!(f, "Authentication required"),
27			AuthError::InvalidToken => write!(f, "Invalid authentication token"),
28			AuthError::Expired => write!(f, "Authentication token expired"),
29			AuthError::InsufficientPermissions => write!(f, "Insufficient permissions"),
30		}
31	}
32}
33
34impl StdError for AuthError {}
35
36pub type AuthResult<T> = Result<T, AuthError>;
37
38pub fn extract_identity_from_auth_header(auth_service: &AuthService, auth_header: &str) -> AuthResult<IdentityId> {
39	if let Some(token) = auth_header.strip_prefix("Bearer ") {
40		validate_bearer_token(auth_service, token.trim())
41	} else if let Some(credentials) = auth_header.strip_prefix("Basic ") {
42		validate_basic_auth(auth_service, credentials.trim())
43	} else {
44		Err(AuthError::InvalidHeader)
45	}
46}
47
48pub fn extract_identity_from_ws_auth(auth_service: &AuthService, token: Option<&str>) -> AuthResult<IdentityId> {
49	match token {
50		Some(t) if !t.is_empty() => validate_bearer_token(auth_service, t),
51		_ => Ok(IdentityId::anonymous()),
52	}
53}
54
55fn validate_bearer_token(auth_service: &AuthService, token: &str) -> AuthResult<IdentityId> {
56	if token.is_empty() {
57		return Err(AuthError::InvalidToken);
58	}
59
60	match auth_service.validate_token(token) {
61		Some(session) => Ok(session.identity),
62		None => Err(AuthError::InvalidToken),
63	}
64}
65
66fn validate_basic_auth(_auth_service: &AuthService, _credentials: &str) -> AuthResult<IdentityId> {
67	// TODO: Implement Basic auth (Base64 decode → username:password → auth_service.authenticate)
68	Err(AuthError::InvalidToken)
69}
70
71#[cfg(test)]
72pub mod tests {
73	use super::*;
74
75	#[test]
76	fn test_auth_error_display() {
77		assert_eq!(AuthError::InvalidHeader.to_string(), "Invalid authorization header");
78		assert_eq!(AuthError::MissingCredentials.to_string(), "Authentication required");
79		assert_eq!(AuthError::InvalidToken.to_string(), "Invalid authentication token");
80		assert_eq!(AuthError::Expired.to_string(), "Authentication token expired");
81	}
82
83	#[test]
84	fn test_anonymous_identity() {
85		let identity = IdentityId::anonymous();
86		assert!(identity.is_anonymous());
87	}
88
89	#[test]
90	fn test_root_identity() {
91		let identity = IdentityId::root();
92		assert!(identity.is_root());
93	}
94}