Common Access Token (CAT) for Rust

A Rust implementation of the Common Access Token (CAT) specification with HMAC signatures.
What is CAT?
Common Access Token (CAT) is a token format designed for authorization and authentication in distributed systems, particularly for media and content delivery applications. It provides a secure, compact, and efficient way to represent claims and authorization information between parties.
CAT is built on established standards:
Overview
This library provides a complete implementation for generating and validating Common Access Tokens (CAT) using HMAC signatures. It is designed to be interoperable with other implementations like node-cat.
Key benefits of using CAT tokens:
- Compact: Binary format results in smaller token sizes compared to text-based formats
- Efficient: CBOR encoding/decoding is faster and requires less processing power
- Secure: Built on established cryptographic standards
- Extensible: Supports custom claims and extensions
Features
-
Token Operations:
- Generate CAT tokens with HMAC signatures (HS256)
- Validate CAT tokens with comprehensive security checks
- Support for token expiration and time-based validation
- Enhanced token handling with FixCoseMac0 for improved compatibility
-
Claims Support:
- Standard CWT claims (issuer, subject, audience, expiration, etc.)
- CAT-specific claims (version, renewal, usage, data, authorization)
- Support for binary data in claims
- Custom claim extension capability
-
Integration:
- Interoperability with other CAT implementations
- Easy integration with Rust applications
- Comprehensive documentation and examples
Installation
Add this to your Cargo.toml
:
[dependencies]
common-access-token = "0.1"
Usage
Token Generation
This example demonstrates how to create a CAT token with standard claims:
use common_access_token::{Cat, CatGenerateOptions, CatOptions, CatValidationType, Claims};
use std::collections::HashMap;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let key = hex::decode("403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d79569388")?;
let mut keys = HashMap::new();
keys.insert("Symmetric256".to_string(), key);
let cat = Cat::new(CatOptions {
keys,
expect_cwt_tag: true,
});
let mut claims = Claims::new();
claims.set_issuer("eyevinn"); claims.set_subject("jonas"); claims.set_audience("one");
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs() as i64;
claims.set_expiration(now + 120); claims.set_issued_at(now);
let token = cat.generate_with_string_kid(claims, "Symmetric256", true)?;
println!("Generated token: {}", token);
Ok(())
}
Using Enhanced Token Handling with FixCoseMac0
For improved compatibility and robustness, the library provides an enhanced implementation via the FixCoseMac0
struct:
use common_access_token::cose::FixCoseMac0;
use common_access_token::Claims;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine;
use std::time::{SystemTime, UNIX_EPOCH};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let key = "your-secret-key".as_bytes();
let kid = "key-1".as_bytes();
let mut claims = Claims::new();
claims.set_issuer("my-service");
claims.set_subject("user-123");
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)?
.as_secs() as i64;
claims.set_expiration(now + 3600);
claims.set_issued_at(now);
let token_bytes = FixCoseMac0::create_token(&claims, key, kid, true)?;
let token = URL_SAFE_NO_PAD.encode(&token_bytes);
println!("Generated token: {}", token);
let received_token_bytes = URL_SAFE_NO_PAD.decode(&token)?;
match FixCoseMac0::verify_token(&received_token_bytes, key) {
Ok(()) => println!("✅ Token is valid"),
Err(err) => println!("❌ Token validation failed: {:?}", err),
}
match FixCoseMac0::extract_claims(&received_token_bytes) {
Ok(validated_claims) => {
println!("Issuer: {:?}", validated_claims.get_issuer());
println!("Subject: {:?}", validated_claims.get_subject());
println!("Expiration: {:?}", validated_claims.get_expiration());
},
Err(err) => println!("Error extracting claims: {:?}", err),
}
let mut new_claims = claims.clone();
new_claims.set_expiration(now + 7200);
let new_token_bytes = FixCoseMac0::resign_token(
&token_bytes, key, kid, &new_claims, true)?;
let new_token = URL_SAFE_NO_PAD.encode(&new_token_bytes);
println!("Re-signed token: {}", new_token);
Ok(())
}
Using CAT-specific Claims
CAT extends the standard CWT claims with additional fields for specific use cases:
use common_access_token::{Cat, CatGenerateOptions, CatOptions, CatValidationType, Claims};
use std::collections::HashMap;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cat = Cat::new(CatOptions {
keys: HashMap::from([
("Symmetric256".to_string(),
hex::decode("403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d79569388")?),
]),
expect_cwt_tag: true,
});
let mut claims = Claims::new();
claims.set_issuer("eyevinn");
claims.set_subject("jonas");
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs() as i64;
claims.set_expiration(now + 3600); claims.set_issued_at(now);
claims.set_cat_version(2);
claims.set_cat_authorization("full_access");
claims.set_cat_usage("streaming");
use std::collections::BTreeMap;
use common_access_token::ClaimValue;
let mut usage_map = BTreeMap::new();
usage_map.insert("type".to_string(), ClaimValue::String("streaming".to_string()));
usage_map.insert("quality".to_string(), ClaimValue::String("hd".to_string()));
usage_map.insert("bitrate".to_string(), ClaimValue::Integer(5000));
claims.set_cat_usage_map(usage_map);
let renewal_json = serde_json::json!({
"url": "https://example.com/renew",
"ttl": 3600
});
claims.set_cat_renewal_from_json(&renewal_json)?;
let token = cat.generate_with_string_kid(claims, "Symmetric256", true)?;
println!("Generated token with CAT claims: {}", token);
Ok(())
}
Token Validation
This example shows how to validate a CAT token and extract its claims:
use common_access_token::{Cat, CatOptions, CatValidationOptions, CatValidationType};
use std::collections::HashMap;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let token = "...";
let key = hex::decode("403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d79569388")?;
let mut keys = HashMap::new();
keys.insert("Symmetric256".to_string(), key);
let cat = Cat::new(CatOptions {
keys,
expect_cwt_tag: true,
});
let validation_options = CatValidationOptions {
issuer: "eyevinn".to_string(), audience: None, };
match cat.validate(token, CatValidationType::Mac, &validation_options) {
Ok(claims) => {
println!("✅ Token is valid!");
println!("Issuer: {:?}", claims.get_issuer());
println!("Subject: {:?}", claims.get_subject());
println!("Audience: {:?}", claims.get_audience());
println!("Expiration: {:?}", claims.get_expiration());
println!("Issued At: {:?}", claims.get_issued_at());
println!("CAT Version: {:?}", claims.get_cat_version());
if let Some(usage_str) = claims.get_cat_usage_string() {
println!("CAT Usage (string): {}", usage_str);
} else if let Some(usage_map) = claims.get_cat_usage_map() {
println!("CAT Usage (map):");
if let Some(type_val) = usage_map.get("type").and_then(|v| v.as_string()) {
println!(" Type: {}", type_val);
}
if let Some(quality_val) = usage_map.get("quality").and_then(|v| v.as_string()) {
println!(" Quality: {}", quality_val);
}
if let Some(bitrate_val) = usage_map.get("bitrate").and_then(|v| v.as_integer()) {
println!(" Bitrate: {}", bitrate_val);
}
}
if let Some(renewal_str) = claims.get_cat_renewal_string() {
println!("CAT Renewal (string): {}", renewal_str);
} else if let Some(renewal_map) = claims.get_cat_renewal_map() {
println!("CAT Renewal (map):");
if let Some(url_val) = renewal_map.get("url").and_then(|v| v.as_string()) {
println!(" URL: {}", url_val);
}
if let Some(ttl_val) = renewal_map.get("ttl").and_then(|v| v.as_integer()) {
println!(" TTL: {}", ttl_val);
}
}
println!("CAT Data: {:?}", claims.get_cat_data_string());
println!("CAT Authorization: {:?}", claims.get_cat_authorization_string());
}
Err(err) => {
eprintln!("❌ Token validation failed: {}", err);
}
}
Ok(())
}
Examples
The library includes several ready-to-use examples in the examples/
directory:
- generate.rs: Demonstrates basic token generation with standard claims
- validate.rs: Shows how to validate tokens and extract claims
- interop.rs: Tests interoperability with the NodeJS implementation
- cat_claims.rs: Demonstrates using CAT-specific claims for advanced use cases, including nested claim structures
- analyze_token.rs: Analyzes a token's structure, extracts claims, and demonstrates re-signing
- debug_token.rs: Provides detailed debugging information for troubleshooting token issues
- binary_claims_test.rs: Shows how to include binary data in token claims
- binary_token_validation.rs: Demonstrates working with binary tokens directly
- fix_validation.rs: Demonstrates using the enhanced FixCoseMac0 implementation for improved token validation
To run an example:
cargo run --example generate
cargo run --example validate <token>
cargo run --example interop [<token>]
cargo run --example cat_claims
cargo run --example analyze_token [<token> [<key_hex>]]
cargo run --example debug_token <token>
Security Considerations
When using CAT tokens in your applications, keep these security best practices in mind:
-
Key Management:
- Store signing keys securely
- Rotate keys periodically
- Use different keys for different environments
-
Token Validation:
- Always validate tokens before trusting their contents
- Check expiration times
- Verify the issuer and audience claims
-
Token Lifetime:
- Use short-lived tokens when possible
- For longer sessions, consider refresh token patterns
-
Claims:
- Only include necessary information in tokens
- Be cautious with sensitive data in claims
Compatibility and Implementation Details
This library is designed to be interoperable with other CAT implementations:
The library includes two complementary implementations:
-
Standard Implementation (CoseMac0
):
- Follows the COSE specification exactly
- Suitable for most standard token operations
- Used by default in the high-level
Cat
API
-
Enhanced Implementation (FixCoseMac0
):
- More robust handling of edge cases
- Improved interoperability with non-standard token formats
- Raw header preservation for more reliable verification
- Specialized methods for token inspection, creation, and re-signing
- Available via the direct
cose::FixCoseMac0
API
Both implementations can be used interchangeably, and the Cat
API will automatically try both approaches when validating tokens, ensuring maximum compatibility.