Rust / Tauri integration guide

Complete the licensing loop in Rust or Tauri with reqwest and ed25519-dalek.

This guide covers the full loop: activate → store → verify on startup → deactivate. It assumes you already have a workspace and a license key.

Dependencies: reqwest (blocking or async), serde_json, ed25519-dalek, base64 and hex-literal.

1. Activate

src/license.rs
use serde_json::json;

const BASE: &str = "https://keygate.unionuser.com";

pub fn activate(license_key: &str, device_id: &str) -> Result<String, Box<dyn std::error::Error>> {
    let client = reqwest::blocking::Client::new();
    let res = client
        .post(format!("{}/api/v1/license/activate", BASE))
        .json(&json!({
            "license_key": license_key,
            "identifier": device_id,
            "identifier_type": "device"
        }))
        .send()?;

    let body: serde_json::Value = res.json()?;
    if body["success"] != true {
        return Err(body["error"]["code"].as_str().unwrap_or("UNKNOWN").into());
    }
    Ok(body["data"]["token"].as_str().unwrap_or_default().to_string())
}

2. Verify offline

src/verify.rs
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use ed25519_dalek::{Signature, VerifyingKey};
use serde_json::Value;

const RAW_PUBLIC: [u8; 32] = hex_literal::hex!("bf17435ee7b937b3592808941683c21d37c4e124d95d4e8fdd86fe775dd23b11");

pub fn verify_token(token: &str, device_id: &str) -> Result<Value, Box<dyn std::error::Error>> {
    let (payload_b64, sig_b64) = token.split_once('.').ok_or("BAD_TOKEN")?;
    let sig_bytes = URL_SAFE_NO_PAD.decode(sig_b64)?;
    let signature = Signature::from_slice(&sig_bytes)?;
    let key = VerifyingKey::from_bytes(&RAW_PUBLIC)?;
    key.verify_strict(payload_b64.as_bytes(), &signature)?;

    let claims: Value = serde_json::from_slice(&URL_SAFE_NO_PAD.decode(payload_b64)?)?;
    if claims["did"] != device_id {
        return Err("DEVICE_MISMATCH".into());
    }
    Ok(claims)
}

3. Deactivate

src/license.rs
pub fn deactivate(license_key: &str, device_id: &str) -> Result<(), Box<dyn std::error::Error>> {
    reqwest::blocking::Client::new()
        .post(format!("{}/api/v1/license/deactivate", BASE))
        .json(&json!({ "license_key": license_key, "identifier": device_id }))
        .send()?;
    Ok(())
}

Tauri notes

  • Keep licensing in Rust commands; expose only capability flags to the frontend.
  • Store the token in app_data_dir, not the frontend localStorage.
  • Prefer ed25519-dalek’s verify_strict to reject weak keys.