firebase-token 0.5.0

Auth with firebase
Documentation
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
                }
            };
        }
    })
}