use std::collections::HashSet;
use std::sync::Arc;
use async_trait::async_trait;
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use rvoip_core_traits::identity::IdentityAssurance;
use rvoip_core_traits::ids::IdentityId;
use serde::Deserialize;
use crate::bearer::{BearerAuthError, BearerValidator};
#[derive(Debug, Deserialize)]
struct Claims {
sub: String,
#[serde(default)]
scope: Option<String>,
#[serde(default)]
scopes: Option<Vec<String>>,
}
pub struct JwtValidator {
decoding_key: DecodingKey,
validation: Validation,
}
impl JwtValidator {
pub fn from_hmac_secret(secret: &[u8]) -> Self {
let mut validation = Validation::new(Algorithm::HS256);
validation.set_audience::<&str>(&[]);
validation.validate_aud = false;
Self {
decoding_key: DecodingKey::from_secret(secret),
validation,
}
}
pub fn from_rsa_pem(pem: &[u8]) -> Result<Self, BearerAuthError> {
let key = DecodingKey::from_rsa_pem(pem)
.map_err(|e| BearerAuthError::Unavailable(format!("invalid RSA PEM: {e}")))?;
let mut validation = Validation::new(Algorithm::RS256);
validation.validate_aud = false;
Ok(Self {
decoding_key: key,
validation,
})
}
pub fn from_ec_pem(pem: &[u8]) -> Result<Self, BearerAuthError> {
let key = DecodingKey::from_ec_pem(pem)
.map_err(|e| BearerAuthError::Unavailable(format!("invalid EC PEM: {e}")))?;
let mut validation = Validation::new(Algorithm::ES256);
validation.validate_aud = false;
Ok(Self {
decoding_key: key,
validation,
})
}
pub fn with_algorithm(mut self, algorithm: Algorithm) -> Self {
self.validation.algorithms = vec![algorithm];
self
}
pub fn with_audience<I, S>(mut self, audiences: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let auds: HashSet<String> = audiences
.into_iter()
.map(|s| s.as_ref().to_string())
.collect();
self.validation.set_audience(&auds.into_iter().collect::<Vec<_>>());
self.validation.validate_aud = true;
self
}
pub fn with_issuer<I, S>(mut self, issuers: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.validation.set_issuer(
&issuers
.into_iter()
.map(|s| s.as_ref().to_string())
.collect::<Vec<_>>(),
);
self
}
pub fn into_arc(self) -> Arc<dyn BearerValidator> {
Arc::new(self)
}
}
#[async_trait]
impl BearerValidator for JwtValidator {
async fn validate(&self, token: &str) -> Result<IdentityAssurance, BearerAuthError> {
if token.is_empty() {
return Err(BearerAuthError::Empty);
}
let data = decode::<Claims>(token, &self.decoding_key, &self.validation)
.map_err(|e| BearerAuthError::Invalid(e.to_string()))?;
let claims = data.claims;
let identity = IdentityId::from_string(claims.sub);
let mut scopes: Vec<String> = Vec::new();
if let Some(scope) = claims.scope {
scopes.extend(scope.split_whitespace().map(|s| s.to_string()));
}
if let Some(list) = claims.scopes {
for s in list {
if !scopes.contains(&s) {
scopes.push(s);
}
}
}
Ok(IdentityAssurance::UserAuthorized {
identity: identity.clone(),
user_id: identity,
scopes,
})
}
}