use jsonwebtoken::{DecodingKey, TokenData, Validation};
use serde::de::DeserializeOwned;
use crate::{Error, JwtDecoder};
#[derive(Clone)]
pub struct LocalDecoder {
keys: Vec<DecodingKey>,
validation: Validation,
}
impl LocalDecoder {
pub fn new(keys: Vec<DecodingKey>, validation: Validation) -> Result<Self, Error> {
if keys.is_empty() {
return Err(Error::Configuration("No decoding keys provided".into()));
}
if validation.algorithms.is_empty() {
return Err(Error::Configuration(
"Validation algorithm is required".into(),
));
}
if validation.aud.is_none() {
return Err(Error::Configuration(
"Validation audience is required".into(),
));
}
Ok(Self { keys, validation })
}
pub fn builder() -> LocalDecoderBuilder {
LocalDecoderBuilder {
keys: None,
validation: None,
}
}
}
pub struct LocalDecoderBuilder {
keys: Option<Vec<DecodingKey>>,
validation: Option<Validation>,
}
impl LocalDecoderBuilder {
pub fn keys(mut self, keys: Vec<DecodingKey>) -> Self {
self.keys = Some(keys);
self
}
pub fn validation(mut self, validation: Validation) -> Self {
self.validation = Some(validation);
self
}
pub fn build(self) -> Result<LocalDecoder, Error> {
let keys = self
.keys
.ok_or_else(|| Error::Configuration("keys are required".into()))?;
let validation = self
.validation
.ok_or_else(|| Error::Configuration("validation is required".into()))?;
LocalDecoder::new(keys, validation)
}
}
impl<T> JwtDecoder<T> for LocalDecoder
where
T: for<'de> DeserializeOwned,
{
fn decode<'a>(
&'a self,
token: &'a str,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<TokenData<T>, Error>> + Send + 'a>>
{
Box::pin(async move {
let mut last_error: Option<Error> = None;
for key in self.keys.iter() {
match jsonwebtoken::decode::<T>(token, key, &self.validation) {
Ok(token_data) => return Ok(token_data),
Err(e) => {
tracing::debug!(error = %e, "failed to decode token with key");
last_error = Some(Error::Jwt(e));
}
}
}
Err(last_error.unwrap_or_else(|| Error::Configuration("No keys available".into())))
})
}
}