micromegas_auth/
types.rs

1use anyhow::Result;
2use chrono::{DateTime, Utc};
3
4/// Authentication type
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub enum AuthType {
7    /// API key authentication
8    ApiKey,
9    /// OIDC authentication
10    Oidc,
11}
12
13/// Authentication context containing user identity and metadata
14#[derive(Debug, Clone)]
15pub struct AuthContext {
16    /// Unique subject identifier (e.g., user ID, service account ID)
17    pub subject: String,
18    /// Email address (if available)
19    pub email: Option<String>,
20    /// Issuer (for OIDC) or "api_key" for API key auth
21    pub issuer: String,
22    /// Audience that was matched during token validation
23    /// - For OIDC access tokens: API audience (e.g., "<https://api.example.com>")
24    /// - For OIDC ID tokens: client ID
25    /// - For API key auth: None
26    pub audience: Option<String>,
27    /// Token expiration time (if applicable)
28    pub expires_at: Option<DateTime<Utc>>,
29    /// Authentication type
30    pub auth_type: AuthType,
31    /// Whether this user has admin privileges
32    pub is_admin: bool,
33    /// Whether this authentication allows user delegation (acting on behalf of others)
34    /// - OIDC user tokens: false (user cannot impersonate others)
35    /// - API keys/service accounts: true (can act on behalf of users)
36    pub allow_delegation: bool,
37}
38
39/// Trait for extracting authentication-relevant data from requests
40pub trait RequestParts: Send + Sync {
41    /// Extract Authorization header as string
42    fn authorization_header(&self) -> Option<&str>;
43
44    /// Extract Bearer token from Authorization header
45    fn bearer_token(&self) -> Option<&str> {
46        self.authorization_header()
47            .and_then(|h| h.strip_prefix("Bearer "))
48    }
49
50    /// Get custom header value by name
51    fn get_header(&self, name: &str) -> Option<&str>;
52
53    /// Get request method (if applicable)
54    fn method(&self) -> Option<&str>;
55
56    /// Get request URI (if applicable)
57    fn uri(&self) -> Option<&str>;
58}
59
60/// HTTP request validation input
61pub struct HttpRequestParts {
62    /// HTTP headers
63    pub headers: http::HeaderMap,
64    /// HTTP method
65    pub method: http::Method,
66    /// Request URI
67    pub uri: http::Uri,
68}
69
70impl RequestParts for HttpRequestParts {
71    fn authorization_header(&self) -> Option<&str> {
72        self.headers
73            .get(http::header::AUTHORIZATION)
74            .and_then(|h| h.to_str().ok())
75    }
76
77    fn get_header(&self, name: &str) -> Option<&str> {
78        self.headers.get(name).and_then(|h| h.to_str().ok())
79    }
80
81    fn method(&self) -> Option<&str> {
82        Some(self.method.as_str())
83    }
84
85    fn uri(&self) -> Option<&str> {
86        Some(self.uri.path())
87    }
88}
89
90/// gRPC request validation input (tonic metadata)
91pub struct GrpcRequestParts {
92    /// gRPC metadata map
93    pub metadata: tonic::metadata::MetadataMap,
94}
95
96impl RequestParts for GrpcRequestParts {
97    fn authorization_header(&self) -> Option<&str> {
98        self.metadata
99            .get("authorization")
100            .and_then(|h| h.to_str().ok())
101    }
102
103    fn get_header(&self, name: &str) -> Option<&str> {
104        self.metadata.get(name).and_then(|h| h.to_str().ok())
105    }
106
107    fn method(&self) -> Option<&str> {
108        None
109    }
110
111    fn uri(&self) -> Option<&str> {
112        None
113    }
114}
115
116/// Trait for authentication providers
117#[async_trait::async_trait]
118pub trait AuthProvider: Send + Sync {
119    /// Validate a request and return authentication context
120    async fn validate_request(&self, parts: &dyn RequestParts) -> Result<AuthContext>;
121}