use serde::{Deserialize, Serialize};
use crate::{
auth::jws::{self, ClientId, GrantId},
capabilities::Capability,
crypto::PublicKey,
keys::Keypair,
};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct GrantClaims {
pub iss: PublicKey,
pub client_id: ClientId,
pub caps: Vec<Capability>,
pub cnf: PublicKey,
pub jti: GrantId,
pub iat: u64,
pub exp: u64,
}
impl GrantClaims {
pub fn decode(compact: &str) -> Result<Self, jws::Error> {
jws::decode_jws_payload(compact)
}
pub fn sign(&self, keypair: &Keypair, jws_type: &str) -> String {
jws::sign_jws(keypair, jws_type, self)
}
}
#[cfg(test)]
mod tests {
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use crate::crypto::Keypair;
use super::*;
#[test]
fn grant_claims_serde_roundtrip() {
let user_kp = Keypair::random();
let client_kp = Keypair::random();
let grant = GrantClaims {
iss: user_kp.public_key(),
client_id: ClientId::new("test.app").unwrap(),
caps: vec![Capability::root()],
cnf: client_kp.public_key(),
jti: GrantId::generate(),
iat: 1700000000,
exp: 1731536000,
};
let json = serde_json::to_string(&grant).unwrap();
let parsed: GrantClaims = serde_json::from_str(&json).unwrap();
assert_eq!(grant, parsed);
}
#[test]
fn grant_claims_decode_from_jws() {
let user_kp = Keypair::random();
let client_kp = Keypair::random();
let grant = GrantClaims {
iss: user_kp.public_key(),
client_id: ClientId::new("test.app").unwrap(),
caps: vec![Capability::root()],
cnf: client_kp.public_key(),
jti: GrantId::generate(),
iat: 1700000000,
exp: 1731536000,
};
let header = URL_SAFE_NO_PAD.encode(b"{\"alg\":\"EdDSA\",\"typ\":\"pubky-grant\"}");
let payload = URL_SAFE_NO_PAD.encode(serde_json::to_vec(&grant).unwrap());
let compact = format!("{}.{}.fakesignature", header, payload);
let decoded = GrantClaims::decode(&compact).unwrap();
assert_eq!(decoded, grant);
}
}