use base64::{prelude::BASE64_URL_SAFE, Engine};
use serde::{Deserialize, Serialize};
use crate::{signer_signature::SignerSignature, signer_user::SignerUser};
#[derive(Debug, Serialize, Deserialize)]
pub struct SignerJWT {
pub header: SignerJWTHeader,
pub claims: SignerJWTClaims,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SignerJWTHeader {
pub typ: String,
pub alg: String,
pub jwk: SignerJWK,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SignerJWTClaims {
pub aud: String,
pub jti: String,
pub iat: i64,
pub exp: i64,
pub nbf: i64,
pub iss: String,
pub sub: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SignerJWK {
pub kty: String,
pub alg: String,
pub crv: String,
pub x: String,
}
impl SignerJWT {
pub fn new(header: SignerJWTHeader, claims: SignerJWTClaims) -> Self {
Self { header, claims }
}
pub fn decode_unverify(str: &str) -> anyhow::Result<Self> {
let split: Vec<&str> = str.split(".").collect();
let header: SignerJWTHeader =
serde_json::from_slice(&BASE64_URL_SAFE.decode(split[0].as_bytes())?)?;
let claims: SignerJWTClaims =
serde_json::from_slice(&BASE64_URL_SAFE.decode(split[1].as_bytes())?)?;
Ok(SignerJWT::new(header, claims))
}
pub fn decode(str: &str) -> anyhow::Result<Self> {
let split: Vec<&str> = str.split(".").collect();
let header: SignerJWTHeader =
serde_json::from_slice(&BASE64_URL_SAFE.decode(split[0].as_bytes())?)?;
let claims: SignerJWTClaims =
serde_json::from_slice(&BASE64_URL_SAFE.decode(split[1].as_bytes())?)?;
let base = [split[0], split[1]].join(".");
let sig_bytes = split[2];
let sig = SignerSignature {
bytes: sig_bytes.to_string(),
pub_key: header.jwk.x.clone(),
};
sig.verify(base.as_bytes())?;
Ok(SignerJWT::new(header, claims))
}
pub fn encode(&self, u: &SignerUser) -> anyhow::Result<String> {
let base = [
BASE64_URL_SAFE.encode(serde_json::to_string(&self.header)?.as_bytes()),
BASE64_URL_SAFE.encode(serde_json::to_string(&self.claims)?.as_bytes()),
]
.join(".");
let sig = SignerSignature::create(u, base.as_bytes())?;
Ok([base, sig.bytes].join("."))
}
}
impl SignerJWTClaims {
pub fn default(aud: String, jti: String) -> Self {
let now = chrono::Local::now();
Self {
aud,
jti,
iat: now.timestamp_millis(),
exp: now
.checked_add_days(chrono::Days::new(7))
.unwrap()
.timestamp_millis(),
nbf: now.timestamp_millis(),
iss: format!(""),
sub: format!(""),
}
}
pub fn with_issuer(mut self, issuer: &str) -> Self {
self.iss = issuer.to_string();
self
}
pub fn with_expired_duration(
mut self,
expired_time: chrono::Duration,
) -> Self {
self.exp = chrono::DateTime::from_timestamp_millis(self.iat)
.unwrap()
.checked_add_signed(expired_time)
.unwrap()
.timestamp_millis();
self
}
pub fn with_subject(mut self, subject: &str) -> Self {
self.sub = subject.to_string();
self
}
}
impl SignerJWTHeader {
pub fn default(u: &SignerUser) -> Self {
Self {
typ: format!("JWT"),
alg: format!("Schnorr SHA3-256"),
jwk: SignerJWK::default(u),
}
}
}
impl SignerJWK {
fn default(u: &SignerUser) -> Self {
Self {
kty: format!("OKP"),
alg: format!("schnorr"),
crv: format!("sr25519"),
x: u.public.pub_key.clone(),
}
}
}