usso 0.2.6

The usso provides a universal single sign-on (SSO) integration for microservices, making it easy to add secure, scalable authentication across different frameworks. This client simplifies the process of connecting any microservice to the USSO service.
Documentation
use std::collections::HashMap;
use std::time::{SystemTime, UNIX_EPOCH};

use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};

use crate::exceptions::USSOError;
use crate::jwks::{fetch_jwks_sync, get_jwk_keys};
use crate::schemas::{JWTConfig, Jwk, Jwks, UserData};

pub fn decode_token(
    key: &Jwk,
    token: &str,
    algorithms: &[Algorithm],
) -> Result<UserData, USSOError> {
    let decoding_key = DecodingKey::from_rsa_components(&key.n, &key.e).unwrap();
    let mut validation = Validation::new(Algorithm::RS256);
    validation.algorithms = algorithms.to_vec();

    match decode::<UserData>(token, &decoding_key, &validation) {
        Ok(token_data) => Ok(token_data.claims),
        Err(_) => Err(USSOError::InvalidToken),
    }
}

pub fn is_expired(token: &str) -> Result<bool, USSOError> {
    let decoded = decode::<HashMap<String, serde_json::Value>>(
        token,
        &DecodingKey::from_secret(&[]),
        &Validation::default(),
    )
    .expect("error in decode");

    let exp = decoded
        .claims
        .get("exp")
        .and_then(|v| v.as_i64())
        .unwrap_or_else(|| {
            SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs() as i64
                + 86400
        });

    Ok(exp
        < SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs() as i64)
}
pub fn decode_token_with_jwks(_jwk_url: &str, token: &str) -> Result<UserData, USSOError> {
    let jwk_keys = get_jwk_keys().expect("Can't get keys");
    let key = jwk_keys
        .match_kid(token)
        .expect("Can't find key with this kid in jwks");
    decode_token(key, token, &[Algorithm::RS256])
}
#[derive(Debug, Clone)]
pub struct Usso {
    jwt_configs: Vec<JWTConfig>,
}

impl Usso {
    pub fn new(jwt_config: Option<JWTConfig>, jwk_url: Option<String>, key: Option<Jwks>) -> Self {
        let jwt_configs = Self::initialize_configs(jwt_config, jwk_url, key);
        Usso { jwt_configs }
    }

    fn initialize_configs(
        jwt_config: Option<JWTConfig>,
        jwk_url: Option<String>,
        key: Option<Jwks>,
    ) -> Vec<JWTConfig> {
        if let Some(config) = jwt_config {
            vec![config]
        } else if let Some(url) = jwk_url {
            let res = Some(fetch_jwks_sync(url.as_str()).unwrap());
            vec![JWTConfig::new(Some(url.clone()), res)]
        } else if let Some(keyset) = key {
            vec![JWTConfig::new(None, Some(keyset))]
        } else {
            panic!("Provide jwt_config, jwk_url, or keys");
        }
    }

    pub fn user_data_from_token(&self, token: &str) -> Result<UserData, USSOError> {
        for config in &self.jwt_configs {
            if let Ok(user_data) = config.decode(token) {
                return Ok(user_data);
            }
        }
        Err(USSOError::Unauthorized)
    }
}