use jsonwebtoken::{Algorithm, DecodingKey, Validation, decode, decode_header}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::env; #[derive(Debug, Deserialize, Serialize, Clone)] pub struct Claims { pub sub: String, pub preferred_username: Option, pub exp: usize, pub iss: String, pub aud: Option, pub realm_access: Option, } #[derive(Debug, Deserialize, Serialize, Clone)] pub struct RealmAccess { pub roles: Vec, } static ISSUER: Lazy = Lazy::new(|| env::var("ISSUER").expect("ISSUER not set")); pub fn validate_token(token: &str, jwks: &Value) -> Result { // 1. Decode header let header = decode_header(token).map_err(|_| "Invalid header")?; let kid = header.kid.ok_or("Missing kid")?; // 2. Find matching key let keys = jwks["keys"].as_array().ok_or("Invalid JWKS")?; let key = keys .iter() .find(|k| k["kid"] == kid) .ok_or("Matching key not found")?; // 3. Extract RSA components let n = key["n"].as_str().ok_or("Missing n")?; let e = key["e"].as_str().ok_or("Missing e")?; let decoding_key = DecodingKey::from_rsa_components(n, e).map_err(|_| "Invalid decoding key")?; // 4. Setup validation rules let mut validation = Validation::new(Algorithm::RS256); validation.set_issuer(&[ISSUER.as_str()]); // Optional but recommended: validation.validate_exp = true; validation.validate_aud = false; // depends on your Keycloak config // 5. Decode & verify let token_data = decode::(token, &decoding_key, &validation) .map_err(|_| "Token validation failed")?; Ok(token_data.claims) }