1use anyhow::Result;
2use chrono::{DateTime, Utc};
3
4#[derive(Debug, Clone, PartialEq, Eq)]
6pub enum AuthType {
7 ApiKey,
9 Oidc,
11}
12
13#[derive(Debug, Clone)]
15pub struct AuthContext {
16 pub subject: String,
18 pub email: Option<String>,
20 pub issuer: String,
22 pub audience: Option<String>,
27 pub expires_at: Option<DateTime<Utc>>,
29 pub auth_type: AuthType,
31 pub is_admin: bool,
33 pub allow_delegation: bool,
37}
38
39pub trait RequestParts: Send + Sync {
41 fn authorization_header(&self) -> Option<&str>;
43
44 fn bearer_token(&self) -> Option<&str> {
46 self.authorization_header()
47 .and_then(|h| h.strip_prefix("Bearer "))
48 }
49
50 fn get_header(&self, name: &str) -> Option<&str>;
52
53 fn method(&self) -> Option<&str>;
55
56 fn uri(&self) -> Option<&str>;
58}
59
60pub struct HttpRequestParts {
62 pub headers: http::HeaderMap,
64 pub method: http::Method,
66 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
90pub struct GrpcRequestParts {
92 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#[async_trait::async_trait]
118pub trait AuthProvider: Send + Sync {
119 async fn validate_request(&self, parts: &dyn RequestParts) -> Result<AuthContext>;
121}