light-openid 2.0.5

Lightweight OpenID primitives & client
Documentation
use crate::client::OpenIDClient;
use crate::errors::{OpenIdError, Res};
use crate::primitives::OpenIDTokenResponse;
use crate::utils::time_utils::time;
use std::time::Duration;

/// A structure that holds a token and helps with its automatic renewal
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenRefresher {
    pub token: OpenIDTokenResponse,
    expires_at: u64,
}

impl TokenRefresher {
    /// Initialize a new token refresher, estimating that the token was issued now
    pub fn from_token(token: OpenIDTokenResponse) -> Option<Self> {
        Some(Self {
            expires_at: time() + token.expires_in?,
            token,
        })
    }

    /// Check out whether this refresh token should be updated or not. Return true if token reached
    /// 75% of its lifetime
    pub fn should_refresh(&self) -> bool {
        self.should_refresh_if_expire_before(Duration::from_secs(
            self.token.expires_in.unwrap_or(15 * 60) / 4,
        ))
    }

    /// Check out whether this refresh token should be updated or not, depending of its upcoming
    /// expiration.
    ///
    /// If no refresh token was returned by IdP, return false
    pub fn should_refresh_if_expire_before(&self, duration: Duration) -> bool {
        if self.token.refresh_token.is_none() {
            return false;
        }

        self.expires_at - duration.as_secs() < time()
    }

    /// Check out whether this refresh token is expired not
    pub fn is_expired(&self) -> bool {
        self.expires_at <= time()
    }

    /// Attempt to refresh this token.
    pub async fn refresh(&mut self, client: &OpenIDClient) -> Res<()> {
        let Some(refresh_token) = &self.token.refresh_token else {
            return Err(OpenIdError::MissingRefreshToken);
        };

        let (token, _) = client
            .request_token_from_refresh_token(refresh_token)
            .await?;

        // Update token information
        self.token.access_token = token.access_token;
        self.token.token_type = token.token_type;

        self.expires_at = time() + token.expires_in.unwrap_or(3600);
        self.token.expires_in = token.expires_in;

        if let Some(new) = token.refresh_token {
            self.token.refresh_token = Some(new);
        }

        if let Some(new) = token.id_token {
            self.token.id_token = Some(new);
        }

        Ok(())
    }
}