use std::{marker::PhantomData, time::SystemTime};
use ed25519_dalek::pkcs8::{EncodePrivateKey, EncodePublicKey};
use jsonwebtoken::{
Algorithm, DecodingKey, TokenData, Validation, decode, errors::Error as JwtError,
};
use pem::Pem;
use serde::Deserialize;
use thiserror::Error;
pub trait Token: std::fmt::Debug + Send + Clone + 'static {
fn id(&self) -> String;
fn exp_time(&self) -> SystemTime;
fn required_claims() -> Vec<&'static str>;
}
pub trait TokenValidator<C>: Send + Sync
where
C: for<'de> Deserialize<'de> + Token + Clone,
{
fn validate(&self, now: SystemTime, token: &str) -> Result<C, TokenValidatorError>;
}
#[derive(Clone, Debug, Error, PartialEq)]
pub enum TokenValidatorError {
#[error("JWT error: {0}")]
JwtError(JwtError),
#[error("JWT signature is invalid")]
JwtSignatureInvalid(),
#[error("token expired: {0:?}")]
TokenExpired(SystemTime),
}
#[derive(Clone)]
pub struct Validator<C>
where
C: for<'de> Deserialize<'de> + Token,
{
public_key: DecodingKey,
validator: Validation,
_marker: PhantomData<fn() -> C>,
}
impl<C> Validator<C>
where
C: for<'de> Deserialize<'de> + Token,
{
pub fn new(public_key: DecodingKey, audience: Option<&[&str]>) -> Self {
let mut validator = Validation::new(Algorithm::EdDSA);
validator.set_required_spec_claims(&C::required_claims());
if let Some(audience) = audience {
validator.set_audience(audience);
}
Self {
public_key,
validator,
_marker: PhantomData,
}
}
}
impl<C> TokenValidator<C> for Validator<C>
where
C: for<'de> Deserialize<'de> + Token,
{
fn validate(&self, now: SystemTime, token: &str) -> Result<C, TokenValidatorError> {
let token_data: TokenData<C> = decode::<C>(token, &self.public_key, &self.validator)?;
let token_exp = token_data.claims.exp_time();
if now > token_exp {
return Err(TokenValidatorError::TokenExpired(token_exp));
}
Ok(token_data.claims)
}
}
pub fn insecure_const_ed25519_key_pair_pem() -> (Pem, Pem) {
let signing_key = insecure_const_ed25519_signing_key();
let public_key = ed25519_dalek::pkcs8::PublicKeyBytes(*signing_key.verifying_key().as_bytes());
let kp = ed25519_dalek::pkcs8::KeypairBytes {
secret_key: *signing_key.as_bytes(),
public_key: Some(public_key),
};
let private_pem = pem::Pem::new("PRIVATE KEY", kp.to_pkcs8_der().unwrap().as_bytes());
let public_pem = pem::Pem::new(
"PUBLIC KEY",
public_key.to_public_key_der().unwrap().as_bytes(),
);
(private_pem, public_pem)
}
pub fn insecure_const_ed25519_signing_key() -> ed25519_dalek::SigningKey {
let seed = [43u8; 32];
ed25519_dalek::SigningKey::from_bytes(&seed)
}
impl From<JwtError> for TokenValidatorError {
fn from(value: JwtError) -> Self {
use jsonwebtoken::errors::*;
if let ErrorKind::InvalidSignature = value.kind() {
return TokenValidatorError::JwtSignatureInvalid();
}
TokenValidatorError::JwtError(value)
}
}