walletconnect-client 0.2.0

WASM library for walletconnect dApp connections
Documentation
use crate::jwt::{
    auth::{error::Error, DEFAULT_TOKEN_AUD},
    claims::basic::JwtBasicClaims,
    decode::client_id::DecodedClientId,
    header::JwtHeader,
};
use chrono::{DateTime, Utc};
use ed25519_dalek::{Signer, SigningKey};
use serde::{Deserialize, Serialize};
use std::{fmt::Display, time::Duration};

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SerializedAuthToken(String);

impl Display for SerializedAuthToken {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

impl From<SerializedAuthToken> for String {
    fn from(value: SerializedAuthToken) -> Self {
        value.0
    }
}

#[derive(Debug, Clone)]
pub struct AuthToken {
    sub: String,
    aud: Option<String>,
    iat: Option<DateTime<Utc>>,
    ttl: Option<Duration>,
}

impl AuthToken {
    pub fn new(sub: impl Into<String>) -> Self {
        Self { sub: sub.into(), aud: None, iat: None, ttl: None }
    }

    pub fn aud(mut self, aud: impl Into<String>) -> Self {
        self.aud = Some(aud.into());
        self
    }

    pub fn iat(mut self, iat: impl Into<Option<DateTime<Utc>>>) -> Self {
        self.iat = iat.into();
        self
    }

    pub fn ttl(mut self, ttl: impl Into<Option<Duration>>) -> Self {
        self.ttl = ttl.into();
        self
    }

    pub fn as_jwt(&self, key: &SigningKey) -> Result<SerializedAuthToken, Error> {
        let iat = self.iat.unwrap_or_else(Utc::now);
        let aud = self.aud.as_deref().unwrap_or(DEFAULT_TOKEN_AUD);

        encode_auth_token(key, &self.sub, aud, iat, self.ttl)
    }
}

pub fn encode_auth_token(
    key: &SigningKey,
    sub: impl Into<String>,
    aud: impl Into<String>,
    iat: DateTime<Utc>,
    ttl: Option<Duration>,
) -> Result<SerializedAuthToken, Error> {
    let encoder = &data_encoding::BASE64URL_NOPAD;

    let exp = ttl
        .map(chrono::Duration::from_std)
        .transpose()
        .map_err(|_| Error::InvalidDuration)?
        .map(|ttl| (iat + ttl).timestamp());

    let claims = {
        let data = JwtBasicClaims {
            iss: DecodedClientId::from_verifying_key(&key.verifying_key()).into(),
            sub: sub.into(),
            aud: aud.into(),
            iat: iat.timestamp(),
            exp,
        };

        encoder.encode(serde_json::to_string(&data)?.as_bytes())
    };

    let header = encoder.encode(serde_json::to_string(&JwtHeader::default())?.as_bytes());
    let message = format!("{header}.{claims}");
    let signature = {
        let data = key.sign(message.as_bytes());
        encoder.encode(&data.to_bytes())
    };

    Ok(SerializedAuthToken(format!("{message}.{signature}")))
}