use std::{sync::Arc, time::Duration};
use serde::Deserialize;
use tokio::{sync::RwLock, task::JoinHandle};
use crate::{
jwk::{JwkKeys, JwkKeysError},
verifier::{JwkVerifier, VerificationError},
};
const FALLBACK_TIMEOUT: Duration = Duration::from_secs(10);
#[derive(Debug, Deserialize)]
pub struct Claims {
pub aud: String,
pub exp: i64,
pub iss: String,
pub sub: String,
pub iat: i64,
}
pub struct FirebaseAuth {
verifier: Arc<RwLock<JwkVerifier>>,
handler: JoinHandle<()>,
}
impl FirebaseAuth {
pub async fn new(project_id: &str) -> Result<Self, JwkKeysError> {
let jwk_keys = JwkKeys::fetch_keys().await?;
let verifier = Arc::new(RwLock::new(JwkVerifier::new(project_id, jwk_keys.keys)));
let handler = keep_key_updated(verifier.clone(), jwk_keys.max_age);
Ok(FirebaseAuth { verifier, handler })
}
pub async fn verify(&self, token: &str) -> Result<Claims, VerificationError> {
let verifier = self.verifier.read().await;
verifier.verify(token)
}
}
impl Drop for FirebaseAuth {
fn drop(&mut self) {
self.handler.abort();
}
}
fn keep_key_updated(
verifier: Arc<RwLock<JwkVerifier>>,
mut delay: Option<Duration>,
) -> JoinHandle<()> {
tokio::spawn(async move {
loop {
let sleep = delay.unwrap_or(FALLBACK_TIMEOUT);
tracing::debug!("Fetcher sleeps {:?}", sleep);
tokio::time::sleep(sleep).await;
delay = match JwkKeys::fetch_keys().await {
Ok(jwk_keys) => {
let mut verifier = verifier.write().await;
verifier.set_keys(jwk_keys.keys);
tracing::debug!(
"Updated JWK keys. Next refresh will be in {:?}",
jwk_keys.max_age
);
jwk_keys.max_age
}
Err(err) => {
tracing::error!("Update JWK Keys Error {:?}", err);
None
}
};
}
})
}