moq-token provides JWT token generation and validation for authenticating MoQ sessions. Available as both a library and CLI tool.
Overview
moq-token supports:
- JWT generation with various signing algorithms
- Path-based authorization for publish/subscribe
- Symmetric keys (HMAC)
- Asymmetric keys (RSA, ECDSA, EdDSA)
- Token validation and verification
Installation
Install the moq-token binary:
cargo install moq-token-cli
The binary is named moq-token.
Library
Add to your Cargo.toml:
[dependencies]
moq-token = "0.5"
CLI Usage
The moq-token CLI generates JWT tokens for authenticating with MoQ relays.
Generate Token
moq-token --key <KEY_FILE> [OPTIONS]
Path to JWK (JSON Web Key) file
Grant full access to a path prefixExample: --root my-app allows publish/subscribe to my-app/*
Grant publish access to specific paths (can be repeated)Example: --pub my-app/stream1 --pub my-app/stream2
Grant subscribe access to specific paths (can be repeated)Example: --sub my-app/* --sub other-app/public
Token expiration time (default: 24h)Examples: 1h, 30m, 7d
Examples
Full Access Token
moq-token --key dev/root.jwk --root my-app
Generates a token with full publish/subscribe access to my-app/*.
Publisher Token
moq-token --key dev/root.jwk \
--pub my-app/stream1 \
--pub my-app/stream2
Generates a token that can publish to specific streams.
Subscriber Token
moq-token --key dev/root.jwk \
--sub my-app/* \
--sub other-app/public
Generates a token that can subscribe to paths (wildcards supported).
Short-lived Token
moq-token --key dev/root.jwk \
--root my-app \
--exp 1h
Generates a token that expires in 1 hour.
Verify Token
Validate a token:
moq-token --verify --key dev/root.jwk --token <TOKEN>
Library Usage
Use moq-token in your Rust application:
Generate Tokens
use moq_token::{TokenGenerator, Claims};
use std::time::Duration;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Load signing key
let key_data = std::fs::read("dev/root.jwk")?;
let generator = TokenGenerator::from_jwk(&key_data)?;
// Create claims
let claims = Claims {
root: Some("my-app".to_string()),
pub_paths: vec![],
sub_paths: vec![],
exp: Some(Duration::from_secs(86400)), // 24 hours
};
// Generate token
let token = generator.generate(&claims)?;
println!("Token: {}", token);
Ok(())
}
Validate Tokens
use moq_token::TokenValidator;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Load public key (or same key for symmetric)
let key_data = std::fs::read("dev/root.jwk")?;
let validator = TokenValidator::from_jwk(&key_data)?;
// Validate token
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOi...";
match validator.validate(token) {
Ok(claims) => {
println!("Valid token!");
println!("Root: {:?}", claims.root);
println!("Pub: {:?}", claims.pub_paths);
println!("Sub: {:?}", claims.sub_paths);
}
Err(e) => eprintln!("Invalid token: {}", e),
}
Ok(())
}
Check Authorization
use moq_token::{TokenValidator, Claims};
fn check_publish_access(claims: &Claims, path: &str) -> bool {
// Check root access
if let Some(root) = &claims.root {
if path.starts_with(root) {
return true;
}
}
// Check specific pub paths
claims.pub_paths.iter().any(|p| path == p)
}
fn check_subscribe_access(claims: &Claims, path: &str) -> bool {
// Check root access
if let Some(root) = &claims.root {
if path.starts_with(root) {
return true;
}
}
// Check sub paths (with wildcard support)
claims.sub_paths.iter().any(|p| {
if p.ends_with("/*") {
path.starts_with(&p[..p.len()-2])
} else {
path == p
}
})
}
Key Management
Generate Keys
Generate a new JWK for signing tokens:
HMAC (Symmetric)
openssl rand -base64 32 > secret.key
Convert to JWK:
{
"kty": "oct",
"k": "<base64-encoded-secret>"
}
RSA (Asymmetric)
# Generate private key
openssl genrsa -out private.pem 2048
# Extract public key
openssl rsa -in private.pem -pubout -out public.pem
# Convert to JWK using online tools or libraries
ECDSA (Asymmetric)
# Generate P-256 key
openssl ecparam -name prime256v1 -genkey -noout -out private.pem
# Extract public key
openssl ec -in private.pem -pubout -out public.pem
EdDSA (Asymmetric)
# Generate Ed25519 key
openssl genpkey -algorithm Ed25519 -out private.pem
# Extract public key
openssl pkey -in private.pem -pubout -out public.pem
Example JWK files:
{
"kty": "oct",
"alg": "HS256",
"k": "your-base64-encoded-secret"
}
Supported Algorithms
Symmetric (HMAC)
- HS256 - HMAC-SHA256
- HS384 - HMAC-SHA384
- HS512 - HMAC-SHA512
Asymmetric (RSA)
- RS256 - RSA-SHA256
- RS384 - RSA-SHA384
- RS512 - RSA-SHA512
- PS256 - RSA-PSS-SHA256
- PS384 - RSA-PSS-SHA384
- PS512 - RSA-PSS-SHA512
Asymmetric (ECDSA)
- ES256 - ECDSA-SHA256 (P-256)
- ES384 - ECDSA-SHA384 (P-384)
Asymmetric (EdDSA)
Token Claims
JWT tokens include the following claims:
{
"root": "my-app", // Full access to prefix
"pub": ["my-app/stream1"], // Publish paths
"sub": ["my-app/*", "other/*"], // Subscribe paths (wildcards)
"exp": 1234567890 // Expiration timestamp
}
Grant full publish/subscribe access to a path prefixExample: "my-app" allows access to my-app/*
List of paths allowed for publishingExample: ["my-app/stream1", "my-app/stream2"]
List of paths allowed for subscribing (supports wildcards)Example: ["my-app/*", "public/*"]
Token expiration time (Unix timestamp)
Integration with moq-relay
Configure moq-relay to use JWT authentication:
[auth]
key = "dev/root.jwk"
public = "anon" # Allow anonymous access to /anon prefix
Clients pass tokens via query parameters:
use moq_native::ClientConfig;
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOi...";
let url = format!("https://relay.example.com/my-app?jwt={}", token);
let client = ClientConfig::default().init()?;
let session = client.connect(url.parse()?).await?;
See moq-relay Authentication for details.
JWKS Loader
Load public keys from a JWKS endpoint (library only):
[dependencies]
moq-token = { version = "0.5", features = ["jwks-loader"] }
use moq_token::JwksLoader;
let loader = JwksLoader::new("https://example.com/.well-known/jwks.json").await?;
let validator = loader.validator_for_token(token)?;
Resources
Next Steps
moq-relay
Configure relay authentication
Authentication Guide
Learn about JWT authentication
moq-cli
Use tokens with the CLI
moq-native
Use tokens in your application