aauth 0.0.1

Rust implementation of the AAuth authorization protocol
Documentation
use async_trait::async_trait;
use jsonwebtoken::{Algorithm, Header, encode};
use uuid::Uuid;

use crate::jwt::{ActClaim, AuthClaims, CnfClaim, ResourceClaims};
use crate::keys::{Ed25519KeyPair, TestKeys};
use crate::types::JwtTyp;

pub trait AuthJwtMinter: Send + Sync {
    fn mint_auth_jwt(
        &self,
        iss: &str,
        aud: &str,
        agent: &str,
        sub: Option<&str>,
        scope: Option<&str>,
    ) -> String;
}

#[derive(Clone)]
pub struct TestAuthJwtMinter {
    keys: TestKeys,
}

impl TestAuthJwtMinter {
    pub fn new(keys: TestKeys) -> Self {
        Self { keys }
    }
}

impl AuthJwtMinter for TestAuthJwtMinter {
    fn mint_auth_jwt(
        &self,
        iss: &str,
        aud: &str,
        agent: &str,
        sub: Option<&str>,
        scope: Option<&str>,
    ) -> String {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs() as i64;

        let claims = AuthClaims {
            iss: iss.into(),
            dwk: "aauth-person.json".into(),
            aud: aud.into(),
            jti: Uuid::new_v4().to_string(),
            agent: agent.into(),
            act: ActClaim { sub: agent.into() },
            cnf: CnfClaim {
                jwk: self.keys.agent_ephemeral.public_jwk(),
            },
            iat: now,
            exp: now + 3600,
            sub: sub.map(str::to_string),
            scope: scope.map(str::to_string),
            tenant: None,
            mission: None,
        };

        let mut header = Header::new(Algorithm::EdDSA);
        header.typ = Some(JwtTyp::Auth.as_str().into());
        header.kid = self.keys.auth_server.kid().map(str::to_string);

        encode(&header, &claims, &self.keys.auth_server.encoding_key()).expect("sign auth jwt")
    }
}

impl TestKeys {
    pub fn auth_jwt_minter(&self) -> TestAuthJwtMinter {
        TestAuthJwtMinter::new(self.clone())
    }

    pub fn resource_token_signer(&self) -> Ed25519ResourceTokenSigner {
        Ed25519ResourceTokenSigner::new(self.resource.clone())
    }
}

#[async_trait]
pub trait ResourceTokenSigner: Send + Sync {
    async fn sign_resource_token(
        &self,
        header: Header,
        claims: ResourceClaims,
    ) -> Result<String, String>;
}

pub struct Ed25519ResourceTokenSigner {
    key: Ed25519KeyPair,
}

impl Ed25519ResourceTokenSigner {
    pub fn new(key: Ed25519KeyPair) -> Self {
        Self { key }
    }
}

#[async_trait]
impl ResourceTokenSigner for Ed25519ResourceTokenSigner {
    async fn sign_resource_token(
        &self,
        mut header: Header,
        claims: ResourceClaims,
    ) -> Result<String, String> {
        if header.kid.is_none() {
            header.kid = self.key.kid().map(str::to_string);
        }
        encode(&header, &claims, &self.key.encoding_key()).map_err(|e| e.to_string())
    }
}

pub fn mint_auth_jwt(
    keys: &TestKeys,
    iss: &str,
    aud: &str,
    agent: &str,
    sub: Option<&str>,
    scope: Option<&str>,
) -> String {
    keys.auth_jwt_minter()
        .mint_auth_jwt(iss, aud, agent, sub, scope)
}