cat_validation/
cat_validation.rs

1use common_access_token::{
2    cat_keys, catm, catr, catreplay, catu, current_timestamp, uri_components, Algorithm, KeyId,
3    RegisteredClaims, TokenBuilder, VerificationOptions,
4};
5use std::collections::BTreeMap;
6
7fn main() {
8    // Create a key for signing and verification
9    let key = b"my-secret-key-for-hmac-sha256";
10    let now = current_timestamp() as i64;
11
12    // Create a token with multiple CAT-specific claims
13    let token = create_token_with_cat_claims(key, now);
14
15    // Encode token to bytes
16    let token_bytes = token.to_bytes().expect("Failed to encode token");
17    println!(
18        "Token with CAT claims encoded to {} bytes",
19        token_bytes.len()
20    );
21
22    // Decode the token
23    let decoded_token =
24        common_access_token::Token::from_bytes(&token_bytes).expect("Failed to decode token");
25
26    // Verify signature
27    decoded_token
28        .verify(key)
29        .expect("Failed to verify signature");
30
31    // Demonstrate different CAT-specific claim validations
32    validate_catu_claim(&decoded_token);
33    validate_catm_claim(&decoded_token);
34    validate_catreplay_claim(&decoded_token);
35}
36
37/// Create a token with multiple CAT-specific claims
38fn create_token_with_cat_claims(key: &[u8], now: i64) -> common_access_token::Token {
39    println!("Creating token with CAT-specific claims...");
40
41    // 1. Create a CATU claim (Common Access Token URI)
42    let mut catu_components = BTreeMap::new();
43
44    // Restrict to https scheme
45    catu_components.insert(uri_components::SCHEME, catu::exact_match("https"));
46
47    // Restrict to example.com host
48    catu_components.insert(uri_components::HOST, catu::suffix_match(".example.com"));
49
50    // Restrict to paths starting with /api
51    catu_components.insert(uri_components::PATH, catu::prefix_match("/api"));
52
53    // Restrict to .json extension
54    catu_components.insert(uri_components::EXTENSION, catu::exact_match(".json"));
55
56    println!("  Added CATU claim with URI restrictions");
57
58    // 2. Create a CATM claim (Common Access Token Methods)
59    let allowed_methods = vec!["GET", "HEAD", "OPTIONS"];
60    println!("  Added CATM claim allowing methods: {:?}", allowed_methods);
61
62    // 3. Create a CATR claim (Common Access Token Renewal)
63    let renewal_params = catr::automatic_renewal(3600, Some(now + 3000));
64    println!("  Added CATR claim with automatic renewal");
65
66    // 4. Create a CATREPLAY claim (prohibit token replay)
67    println!("  Added CATREPLAY claim prohibiting token replay");
68
69    // Build the token with all CAT-specific claims
70    TokenBuilder::new()
71        .algorithm(Algorithm::HmacSha256)
72        .protected_key_id(KeyId::string("example-key-id"))
73        .registered_claims(
74            RegisteredClaims::new()
75                .with_issuer("example-issuer")
76                .with_subject("example-subject")
77                .with_audience("example-audience")
78                .with_expiration(now as u64 + 3600) // 1 hour from now
79                .with_not_before(now as u64)
80                .with_issued_at(now as u64)
81                .with_cti(b"token-id-1234".to_vec()),
82        )
83        // Add CAT-specific claims
84        .custom_cbor(cat_keys::CATU, catu::create(catu_components))
85        .custom_cbor(cat_keys::CATR, catr::create(renewal_params))
86        .custom_cbor(cat_keys::CATREPLAY, catreplay::prohibited())
87        .custom_array(cat_keys::CATM, catm::create(allowed_methods))
88        .sign(key)
89        .expect("Failed to sign token")
90}
91
92/// Validate the CATU claim against different URIs
93fn validate_catu_claim(token: &common_access_token::Token) {
94    println!("\nValidating CATU (URI) claim:");
95
96    // Define URIs to test
97    let valid_uri = "https://api.example.com/api/users.json";
98    let invalid_scheme_uri = "http://api.example.com/api/users.json";
99    let invalid_host_uri = "https://api.other-site.com/api/users.json";
100    let invalid_path_uri = "https://api.example.com/users.json";
101    let invalid_extension_uri = "https://api.example.com/api/users.xml";
102
103    // Test valid URI
104    let options = VerificationOptions::new().verify_catu(true).uri(valid_uri);
105
106    match token.verify_claims(&options) {
107        Ok(_) => println!("  VALID URI: {}", valid_uri),
108        Err(e) => println!(
109            "  ERROR: {} should be valid, but got error: {}",
110            valid_uri, e
111        ),
112    }
113
114    // Test invalid scheme
115    let invalid_scheme_options = VerificationOptions::new()
116        .verify_catu(true)
117        .uri(invalid_scheme_uri);
118
119    match token.verify_claims(&invalid_scheme_options) {
120        Ok(_) => println!(
121            "  ERROR: {} should be invalid (wrong scheme)",
122            invalid_scheme_uri
123        ),
124        Err(e) => println!(
125            "  INVALID URI (as expected): {} - Error: {}",
126            invalid_scheme_uri, e
127        ),
128    }
129
130    // Test invalid host
131    let invalid_host_options = VerificationOptions::new()
132        .verify_catu(true)
133        .uri(invalid_host_uri);
134
135    match token.verify_claims(&invalid_host_options) {
136        Ok(_) => println!(
137            "  ERROR: {} should be invalid (wrong host)",
138            invalid_host_uri
139        ),
140        Err(e) => println!(
141            "  INVALID URI (as expected): {} - Error: {}",
142            invalid_host_uri, e
143        ),
144    }
145
146    // Test invalid path
147    let invalid_path_options = VerificationOptions::new()
148        .verify_catu(true)
149        .uri(invalid_path_uri);
150
151    match token.verify_claims(&invalid_path_options) {
152        Ok(_) => println!(
153            "  ERROR: {} should be invalid (wrong path)",
154            invalid_path_uri
155        ),
156        Err(e) => println!(
157            "  INVALID URI (as expected): {} - Error: {}",
158            invalid_path_uri, e
159        ),
160    }
161
162    // Test invalid extension
163    let invalid_extension_options = VerificationOptions::new()
164        .verify_catu(true)
165        .uri(invalid_extension_uri);
166
167    match token.verify_claims(&invalid_extension_options) {
168        Ok(_) => println!(
169            "  ERROR: {} should be invalid (wrong extension)",
170            invalid_extension_uri
171        ),
172        Err(e) => println!(
173            "  INVALID URI (as expected): {} - Error: {}",
174            invalid_extension_uri, e
175        ),
176    }
177}
178
179/// Validate the CATM claim against different HTTP methods
180fn validate_catm_claim(token: &common_access_token::Token) {
181    println!("\nValidating CATM (HTTP Methods) claim:");
182
183    // Test allowed methods
184    for method in &["GET", "HEAD", "OPTIONS"] {
185        let options = VerificationOptions::new()
186            .verify_catm(true)
187            .http_method(*method);
188
189        match token.verify_claims(&options) {
190            Ok(_) => println!("  VALID METHOD: {}", method),
191            Err(e) => println!("  ERROR: {} should be valid, but got error: {}", method, e),
192        }
193    }
194
195    // Test disallowed methods
196    for method in &["POST", "PUT", "DELETE", "PATCH"] {
197        let options = VerificationOptions::new()
198            .verify_catm(true)
199            .http_method(*method);
200
201        match token.verify_claims(&options) {
202            Ok(_) => println!("  ERROR: {} should be invalid method", method),
203            Err(e) => println!("  INVALID METHOD (as expected): {} - Error: {}", method, e),
204        }
205    }
206}
207
208/// Validate the CATREPLAY claim
209fn validate_catreplay_claim(token: &common_access_token::Token) {
210    println!("\nValidating CATREPLAY claim:");
211
212    // Test with token not seen before (should pass)
213    let options_not_seen = VerificationOptions::new()
214        .verify_catreplay(true)
215        .token_seen_before(false);
216
217    match token.verify_claims(&options_not_seen) {
218        Ok(_) => println!("  VALID: Token not seen before is accepted (as expected)"),
219        Err(e) => println!(
220            "  ERROR: Token not seen before should be valid, but got error: {}",
221            e
222        ),
223    }
224
225    // Test with token seen before (should fail with replay prohibited)
226    let options_seen = VerificationOptions::new()
227        .verify_catreplay(true)
228        .token_seen_before(true);
229
230    match token.verify_claims(&options_seen) {
231        Ok(_) => println!("  ERROR: Token seen before should be rejected"),
232        Err(e) => println!(
233            "  INVALID (as expected): Token seen before is rejected - Error: {}",
234            e
235        ),
236    }
237}