use common_access_token::{
cat_keys, catm, catr, catreplay, catu, current_timestamp, uri_components, Algorithm, KeyId,
RegisteredClaims, TokenBuilder, VerificationOptions,
};
use std::collections::BTreeMap;
fn main() {
let key = b"my-secret-key-for-hmac-sha256";
let now = current_timestamp() as i64;
let token = create_token_with_cat_claims(key, now);
let token_bytes = token.to_bytes().expect("Failed to encode token");
println!(
"Token with CAT claims encoded to {} bytes",
token_bytes.len()
);
let decoded_token =
common_access_token::Token::from_bytes(&token_bytes).expect("Failed to decode token");
decoded_token
.verify(key)
.expect("Failed to verify signature");
validate_catu_claim(&decoded_token);
validate_catm_claim(&decoded_token);
validate_catreplay_claim(&decoded_token);
}
fn create_token_with_cat_claims(key: &[u8], now: i64) -> common_access_token::Token {
println!("Creating token with CAT-specific claims...");
let mut catu_components = BTreeMap::new();
catu_components.insert(uri_components::SCHEME, catu::exact_match("https"));
catu_components.insert(uri_components::HOST, catu::suffix_match(".example.com"));
catu_components.insert(uri_components::PATH, catu::prefix_match("/api"));
catu_components.insert(uri_components::EXTENSION, catu::exact_match(".json"));
println!(" Added CATU claim with URI restrictions");
let allowed_methods = vec!["GET", "HEAD", "OPTIONS"];
println!(" Added CATM claim allowing methods: {:?}", allowed_methods);
let renewal_params = catr::automatic_renewal(3600, Some(now + 3000));
println!(" Added CATR claim with automatic renewal");
println!(" Added CATREPLAY claim prohibiting token replay");
TokenBuilder::new()
.algorithm(Algorithm::HmacSha256)
.protected_key_id(KeyId::string("example-key-id"))
.registered_claims(
RegisteredClaims::new()
.with_issuer("example-issuer")
.with_subject("example-subject")
.with_audience("example-audience")
.with_expiration(now as u64 + 3600) .with_not_before(now as u64)
.with_issued_at(now as u64)
.with_cti(b"token-id-1234".to_vec()),
)
.custom_cbor(cat_keys::CATU, catu::create(catu_components))
.custom_cbor(cat_keys::CATR, catr::create(renewal_params))
.custom_cbor(cat_keys::CATREPLAY, catreplay::prohibited())
.custom_array(cat_keys::CATM, catm::create(allowed_methods))
.sign(key)
.expect("Failed to sign token")
}
fn validate_catu_claim(token: &common_access_token::Token) {
println!("\nValidating CATU (URI) claim:");
let valid_uri = "https://api.example.com/api/users.json";
let invalid_scheme_uri = "http://api.example.com/api/users.json";
let invalid_host_uri = "https://api.other-site.com/api/users.json";
let invalid_path_uri = "https://api.example.com/users.json";
let invalid_extension_uri = "https://api.example.com/api/users.xml";
let options = VerificationOptions::new().verify_catu(true).uri(valid_uri);
match token.verify_claims(&options) {
Ok(_) => println!(" VALID URI: {}", valid_uri),
Err(e) => println!(
" ERROR: {} should be valid, but got error: {}",
valid_uri, e
),
}
let invalid_scheme_options = VerificationOptions::new()
.verify_catu(true)
.uri(invalid_scheme_uri);
match token.verify_claims(&invalid_scheme_options) {
Ok(_) => println!(
" ERROR: {} should be invalid (wrong scheme)",
invalid_scheme_uri
),
Err(e) => println!(
" INVALID URI (as expected): {} - Error: {}",
invalid_scheme_uri, e
),
}
let invalid_host_options = VerificationOptions::new()
.verify_catu(true)
.uri(invalid_host_uri);
match token.verify_claims(&invalid_host_options) {
Ok(_) => println!(
" ERROR: {} should be invalid (wrong host)",
invalid_host_uri
),
Err(e) => println!(
" INVALID URI (as expected): {} - Error: {}",
invalid_host_uri, e
),
}
let invalid_path_options = VerificationOptions::new()
.verify_catu(true)
.uri(invalid_path_uri);
match token.verify_claims(&invalid_path_options) {
Ok(_) => println!(
" ERROR: {} should be invalid (wrong path)",
invalid_path_uri
),
Err(e) => println!(
" INVALID URI (as expected): {} - Error: {}",
invalid_path_uri, e
),
}
let invalid_extension_options = VerificationOptions::new()
.verify_catu(true)
.uri(invalid_extension_uri);
match token.verify_claims(&invalid_extension_options) {
Ok(_) => println!(
" ERROR: {} should be invalid (wrong extension)",
invalid_extension_uri
),
Err(e) => println!(
" INVALID URI (as expected): {} - Error: {}",
invalid_extension_uri, e
),
}
}
fn validate_catm_claim(token: &common_access_token::Token) {
println!("\nValidating CATM (HTTP Methods) claim:");
for method in &["GET", "HEAD", "OPTIONS"] {
let options = VerificationOptions::new()
.verify_catm(true)
.http_method(*method);
match token.verify_claims(&options) {
Ok(_) => println!(" VALID METHOD: {}", method),
Err(e) => println!(" ERROR: {} should be valid, but got error: {}", method, e),
}
}
for method in &["POST", "PUT", "DELETE", "PATCH"] {
let options = VerificationOptions::new()
.verify_catm(true)
.http_method(*method);
match token.verify_claims(&options) {
Ok(_) => println!(" ERROR: {} should be invalid method", method),
Err(e) => println!(" INVALID METHOD (as expected): {} - Error: {}", method, e),
}
}
}
fn validate_catreplay_claim(token: &common_access_token::Token) {
println!("\nValidating CATREPLAY claim:");
let options_not_seen = VerificationOptions::new()
.verify_catreplay(true)
.token_seen_before(false);
match token.verify_claims(&options_not_seen) {
Ok(_) => println!(" VALID: Token not seen before is accepted (as expected)"),
Err(e) => println!(
" ERROR: Token not seen before should be valid, but got error: {}",
e
),
}
let options_seen = VerificationOptions::new()
.verify_catreplay(true)
.token_seen_before(true);
match token.verify_claims(&options_seen) {
Ok(_) => println!(" ERROR: Token seen before should be rejected"),
Err(e) => println!(
" INVALID (as expected): Token seen before is rejected - Error: {}",
e
),
}
}