genius-core-client 0.4.0

Genius Core Client Library. Written in Rust and using PyO3 for Python bindings.
Documentation
use crate::types::error::{ErrorCode, HstpError};
use jsonwebtoken::{decode_header, jwk::AlgorithmParameters, DecodingKey, Validation};
use serde::{Deserialize, Serialize};

use super::jwks::JWKS_KEY_CONTENTS;

#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
    pub sub: String, // user id
    pub exp: u32,    // expiry timestamp
    pub iat: u32,    // issued timestamp
}

pub(crate) async fn decode_jwt(token: &str) -> Result<Claims, HstpError> {
    let header = decode_header(token).map_err(HstpError::from_error)?;
    let key_id = header.kid.ok_or_else(|| {
        HstpError::new(
            ErrorCode::None,
            "Failed to decode JWT, key id not found".to_string(),
            "".into(),
        )
    })?;

    let jwks = JWKS_KEY_CONTENTS
        .lock()
        .expect("Unable to get JWKS_KEY_CONTENTS lock")
        .clone();

    let jwks = match jwks {
        Some(jwks) => jwks,
        None => {
            return Err(HstpError::new(
                ErrorCode::None,
                "Failed to decode JWT, JWKS not set".to_string(),
                "".into(),
            ))
        }
    };

    let key = jwks.find(&key_id).ok_or_else(|| {
        HstpError::new(
            ErrorCode::None,
            "Failed to decode JWT, key not found".to_string(),
            "".into(),
        )
    })?;

    let rsa = match &key.algorithm {
        AlgorithmParameters::RSA(rsa) => rsa,
        _ => {
            return Err(HstpError::new(
                ErrorCode::None,
                "Failed to decode JWT, algorithm not supported".to_string(),
                "".into(),
            ))
        }
    };

    let decoding_key = DecodingKey::from_rsa_components(&rsa.n, &rsa.e).map_err(|e| {
        HstpError::new(
            ErrorCode::None,
            format!("Failed to decode JWT from_rsa_components, {}", e),
            "".into(),
        )
    })?;

    let validation = &mut Validation::new(header.alg);
    validation.validate_exp = false;
    validation.validate_aud = false;

    let decoded_token =
        jsonwebtoken::decode::<Claims>(token, &decoding_key, validation).map_err(|e| {
            HstpError::new(
                ErrorCode::None,
                format!("Failed to decode JWT (jsonwebtoken::decode): {}", e),
                "".into(),
            )
        })?;

    Ok(decoded_token.claims)
}