fire_auth_token/
structs.rs1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::error::Error;
4use std::fmt;
5use std::sync::Arc;
6use tokio::sync::RwLock;
7
8#[derive(Debug, Clone)]
10pub struct SharedState {
11 pub keys: PublicKeysResponse,
12 pub expiry: i64, }
14
15#[derive(Debug, Serialize, Deserialize)]
17pub struct FirebaseAuthUser {
18 pub exp: i64,
19 pub iat: i64,
20 pub aud: String,
21 pub iss: String,
22 pub sub: String,
23 pub auth_time: i64,
24}
25
26#[derive(Debug, Serialize, Deserialize)]
28pub struct FirebaseAuthGoogleUser {
29 pub name: Option<String>,
30 pub picture: Option<String>,
31 pub iss: Option<String>,
32 pub aud: Option<String>,
33 pub auth_time: Option<i64>,
34 pub user_id: Option<String>,
35 pub sub: Option<String>,
36 pub iat: Option<i64>,
37 pub exp: Option<i64>,
38 pub email: Option<String>,
39 pub email_verified: Option<bool>,
40 pub firebase: Option<FirebaseGoogleUserData>,
41}
42
43#[derive(Debug, Serialize, Deserialize)]
44pub struct FirebaseGoogleUserData {
45 pub identities: Option<HashMap<String, Vec<String>>>,
46 pub sign_in_provider: Option<String>,
47}
48
49#[derive(Debug, Deserialize, Serialize)]
51pub struct FirebaseTokenHeader {
52 pub alg: String,
54 pub kid: String,
56}
57
58#[derive(Debug, Deserialize, Clone)]
60pub struct PublicKeysResponse {
61 #[serde(flatten)]
62 pub keys: HashMap<String, String>,
63}
64
65#[derive(Debug, Clone)]
67pub struct FirebaseAuthConfig {
68 pub project_id: String,
70 pub public_keys_url: String,
72}
73
74#[derive(Debug)]
76pub struct FirebaseAuth {
77 pub config: FirebaseAuthConfig,
79 pub cached_public_keys: Arc<RwLock<Option<SharedState>>>,
81}
82
83#[derive(Debug)]
85pub enum FirebaseAuthError {
86 InvalidTokenFormat,
87 TokenExpired,
88 InvalidSignature,
89 InvalidIssuer,
90 InvalidAudience,
91 InvalidSubject,
92 InvalidAuthTime,
93 HttpError(String),
94 JwtError(String),
95}
96
97impl fmt::Display for FirebaseAuthError {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 match self {
101 FirebaseAuthError::InvalidTokenFormat => write!(f, "Invalid token format"),
102 FirebaseAuthError::TokenExpired => write!(f, "Token expired"),
103 FirebaseAuthError::InvalidSignature => write!(f, "Invalid signature"),
104 FirebaseAuthError::InvalidIssuer => write!(f, "Invalid issuer"),
105 FirebaseAuthError::InvalidAudience => write!(f, "Invalid audience"),
106 FirebaseAuthError::InvalidSubject => write!(f, "Invalid subject"),
107 FirebaseAuthError::InvalidAuthTime => write!(f, "Invalid authentication time"),
108 FirebaseAuthError::HttpError(msg) => write!(f, "HTTP request failed: {}", msg),
109 FirebaseAuthError::JwtError(msg) => write!(f, "JWT error: {}", msg),
110 }
111 }
112}
113
114impl Error for FirebaseAuthError {
116 fn source(&self) -> Option<&(dyn Error + 'static)> {
117 None
118 }
119}
120
121pub type FirebaseAuthResult<T> = Result<T, FirebaseAuthError>;
123
124pub trait TokenVerifier {
126 fn verify(&self, project_id: &str, current_time: i64) -> FirebaseAuthResult<()>;
127}
128
129impl TokenVerifier for FirebaseAuthUser {
130 fn verify(&self, project_id: &str, current_time: i64) -> FirebaseAuthResult<()> {
131 if self.exp <= current_time {
132 return Err(FirebaseAuthError::TokenExpired);
133 }
134
135 if self.iat >= current_time {
136 return Err(FirebaseAuthError::InvalidTokenFormat);
137 }
138
139 if self.auth_time >= current_time {
140 return Err(FirebaseAuthError::InvalidAuthTime);
141 }
142
143 if self.aud != project_id {
144 return Err(FirebaseAuthError::InvalidAudience);
145 }
146
147 let expected_issuer = format!("https://securetoken.google.com/{}", project_id);
148 if self.iss != expected_issuer {
149 return Err(FirebaseAuthError::InvalidIssuer);
150 }
151
152 if self.sub.is_empty() {
153 return Err(FirebaseAuthError::InvalidSubject);
154 }
155
156 Ok(())
157 }
158}
159
160impl TokenVerifier for FirebaseAuthGoogleUser {
161 fn verify(&self, project_id: &str, current_time: i64) -> FirebaseAuthResult<()> {
162 if let Some(exp) = self.exp {
163 if exp <= current_time {
164 return Err(FirebaseAuthError::TokenExpired);
165 }
166 }
167
168 if let Some(iat) = self.iat {
169 if iat >= current_time {
170 return Err(FirebaseAuthError::InvalidTokenFormat);
171 }
172 }
173
174 if let Some(auth_time) = self.auth_time {
175 if auth_time >= current_time {
176 return Err(FirebaseAuthError::InvalidAuthTime);
177 }
178 }
179
180 if let Some(aud) = &self.aud {
181 if aud != project_id {
182 return Err(FirebaseAuthError::InvalidAudience);
183 }
184 }
185
186 if let Some(iss) = &self.iss {
187 let expected_issuer = format!("https://securetoken.google.com/{}", project_id);
188 if iss != &expected_issuer {
189 return Err(FirebaseAuthError::InvalidIssuer);
190 }
191 }
192
193 if let Some(sub) = &self.sub {
194 if sub.is_empty() {
195 return Err(FirebaseAuthError::InvalidSubject);
196 }
197 } else {
198 return Err(FirebaseAuthError::InvalidSubject);
199 }
200
201 Ok(())
202 }
203}