hessra_token/
mint.rs

1extern crate biscuit_auth as biscuit;
2
3use crate::verify::{biscuit_key_from_string, ServiceNode};
4
5use biscuit::macros::{biscuit, rule};
6use biscuit::{Biscuit, BiscuitBuilder, KeyPair};
7use chrono::Utc;
8use std::error::Error;
9use tracing::info;
10
11/// TokenTimeConfig allows control over token creation times and durations
12/// This is used to create tokens with custom start times and durations
13/// for testing purposes. In the future, this can be enhanced to support
14/// variable length tokens, such as long-lived bearer tokens.
15#[derive(Debug, Clone, Copy)]
16pub struct TokenTimeConfig {
17    /// Optional custom start time (now time override)
18    pub start_time: Option<i64>,
19    /// Duration in seconds (default: 300 seconds = 5 minutes)
20    pub duration: i64,
21}
22
23impl Default for TokenTimeConfig {
24    fn default() -> Self {
25        Self {
26            start_time: None,
27            duration: 300, // 5 minutes in seconds
28        }
29    }
30}
31
32fn _create_base_biscuit_builder(
33    subject: String,
34    resource: String,
35    operation: String,
36) -> Result<BiscuitBuilder, Box<dyn Error>> {
37    create_base_biscuit_builder_with_time(subject, resource, operation, TokenTimeConfig::default())
38}
39
40fn create_base_biscuit_builder_with_time(
41    subject: String,
42    resource: String,
43    operation: String,
44    time_config: TokenTimeConfig,
45) -> Result<BiscuitBuilder, Box<dyn Error>> {
46    let start_time = time_config
47        .start_time
48        .unwrap_or_else(|| Utc::now().timestamp());
49    let expiration = start_time + time_config.duration;
50
51    let biscuit_builder = biscuit!(
52        r#"
53            right({subject}, {resource}, {operation});
54            check if time($time), $time < {expiration};
55        "#
56    );
57
58    Ok(biscuit_builder)
59}
60
61pub fn create_raw_biscuit(
62    subject: String,
63    resource: String,
64    operation: String,
65    key: KeyPair,
66    time_config: TokenTimeConfig,
67) -> Result<Biscuit, Box<dyn Error>> {
68    let biscuit = create_base_biscuit_builder_with_time(subject, resource, operation, time_config)?
69        .build(&key)?;
70
71    info!("biscuit (authority): {}", biscuit);
72
73    Ok(biscuit)
74}
75
76/// Creates a new biscuit token with the specified subject and resource.
77///
78/// This function creates a token that grants read and write access to the specified resource
79/// for the given subject. The token will be valid for 5 minutes by default.
80///
81/// # Arguments
82///
83/// * `subject` - The subject (user) identifier
84/// * `resource` - The resource identifier to grant access to
85/// * `operation` - The operation to grant access to
86/// * `key` - The key pair used to sign the token
87/// * `time_config` - Optional time configuration for token validity
88///
89/// # Returns
90///
91/// * `Ok(Vec<u8>)` - The binary token data if successful
92/// * `Err(Box<dyn Error>)` - If token creation fails
93pub fn create_biscuit(
94    subject: String,
95    resource: String,
96    operation: String,
97    key: KeyPair,
98    time_config: TokenTimeConfig,
99) -> Result<Vec<u8>, Box<dyn Error>> {
100    let biscuit = create_raw_biscuit(subject, resource, operation, key, time_config)?;
101    let token = biscuit.to_vec()?;
102    Ok(token)
103}
104
105fn create_base64_biscuit(
106    subject: String,
107    resource: String,
108    operation: String,
109    key: KeyPair,
110    time_config: TokenTimeConfig,
111) -> Result<String, Box<dyn Error>> {
112    let biscuit = create_raw_biscuit(subject, resource, operation, key, time_config)?;
113    let token = biscuit.to_base64()?;
114    Ok(token)
115}
116
117pub fn create_token(
118    subject: String,
119    resource: String,
120    operation: String,
121    key: KeyPair,
122) -> Result<String, Box<dyn Error>> {
123    create_base64_biscuit(
124        subject,
125        resource,
126        operation,
127        key,
128        TokenTimeConfig::default(),
129    )
130}
131
132pub fn create_token_with_time(
133    subject: String,
134    resource: String,
135    operation: String,
136    key: KeyPair,
137    time_config: TokenTimeConfig,
138) -> Result<String, Box<dyn Error>> {
139    create_base64_biscuit(subject, resource, operation, key, time_config)
140}
141
142/// Creates a new biscuit token with service chain attestations.
143/// Creates a new biscuit token with service chain attestations.
144///
145/// This function creates a token that grants access to the specified resource for the given subject,
146/// and includes attestations for each service node in the chain. The token will be valid for 5 minutes by default.
147///
148/// # Arguments
149///
150/// * `subject` - The subject (user) identifier
151/// * `resource` - The resource identifier to grant access to
152/// * `operation` - The operation to grant access to
153/// * `key` - The key pair used to sign the token
154/// * `nodes` - Vector of service nodes that will attest to the token
155///
156/// # Returns
157///
158/// * `Ok(Vec<u8>)` - The binary token data if successful
159/// * `Err(Box<dyn Error>)` - If token creation fails
160pub fn create_service_chain_biscuit(
161    subject: String,
162    resource: String,
163    operation: String,
164    key: KeyPair,
165    nodes: &Vec<ServiceNode>,
166    time_config: TokenTimeConfig,
167) -> Result<Vec<u8>, Box<dyn Error>> {
168    let biscuit =
169        create_raw_service_chain_biscuit(subject, resource, operation, key, nodes, time_config)?;
170    let token = biscuit.to_vec()?;
171    Ok(token)
172}
173
174fn create_base64_service_chain_biscuit(
175    subject: String,
176    resource: String,
177    operation: String,
178    key: KeyPair,
179    nodes: &Vec<ServiceNode>,
180    time_config: TokenTimeConfig,
181) -> Result<String, Box<dyn Error>> {
182    let biscuit =
183        create_raw_service_chain_biscuit(subject, resource, operation, key, nodes, time_config)?;
184    let token = biscuit.to_base64()?;
185    Ok(token)
186}
187
188pub fn create_raw_service_chain_biscuit(
189    subject: String,
190    resource: String,
191    operation: String,
192    key: KeyPair,
193    nodes: &Vec<ServiceNode>,
194    time_config: TokenTimeConfig,
195) -> Result<Biscuit, Box<dyn Error>> {
196    create_service_chain_biscuit_with_time(subject, resource, operation, key, nodes, time_config)
197}
198
199pub fn create_service_chain_token(
200    subject: String,
201    resource: String,
202    operation: String,
203    key: KeyPair,
204    nodes: &Vec<ServiceNode>,
205) -> Result<String, Box<dyn Error>> {
206    create_base64_service_chain_biscuit(
207        subject,
208        resource,
209        operation,
210        key,
211        nodes,
212        TokenTimeConfig::default(),
213    )
214}
215
216/// Creates a new biscuit token with service chain attestations and custom time settings.
217///
218/// This function creates a token that grants access to the specified resource for the given subject,
219/// includes attestations for each service node in the chain, and allows custom time configuration.
220///
221/// # Arguments
222///
223/// * `subject` - The subject (user) identifier
224/// * `resource` - The resource identifier to grant access to
225/// * `operation` - The operation to grant access to
226/// * `key` - The key pair used to sign the token
227/// * `nodes` - Vector of service nodes that will attest to the token
228/// * `time_config` - Time configuration for token validity
229///
230/// # Returns
231///
232/// * `Ok(Vec<u8>)` - The binary token data if successful
233/// * `Err(Box<dyn Error>)` - If token creation fails
234pub fn create_service_chain_biscuit_with_time(
235    subject: String,
236    resource: String,
237    operation: String,
238    key: KeyPair,
239    nodes: &Vec<ServiceNode>,
240    time_config: TokenTimeConfig,
241) -> Result<Biscuit, Box<dyn Error>> {
242    let service = resource.clone();
243    let mut biscuit_builder =
244        create_base_biscuit_builder_with_time(subject, service, operation, time_config)?;
245
246    // Add each node in the service chain to the biscuit builder
247    for node in nodes {
248        let component = node.component.clone();
249        let public_key = biscuit_key_from_string(node.public_key.clone())?;
250        biscuit_builder = biscuit_builder.rule(rule!(
251            r#"
252                node($s, {component}) <- service($s) trusting {public_key};
253            "#
254        ))?;
255    }
256
257    let biscuit = biscuit_builder.build(&key)?;
258
259    info!("biscuit (authority): {}", biscuit);
260
261    Ok(biscuit)
262}
263
264pub fn create_service_chain_token_with_time(
265    subject: String,
266    resource: String,
267    operation: String,
268    key: KeyPair,
269    nodes: &Vec<ServiceNode>,
270    time_config: TokenTimeConfig,
271) -> Result<String, Box<dyn Error>> {
272    let biscuit = create_service_chain_biscuit_with_time(
273        subject,
274        resource,
275        operation,
276        key,
277        nodes,
278        time_config,
279    )?;
280    let token = biscuit.to_base64()?;
281    Ok(token)
282}
283
284#[cfg(test)]
285mod tests {
286    use super::*;
287    use crate::verify::{verify_biscuit_local, verify_service_chain_biscuit_local};
288    use biscuit::macros::block;
289    use biscuit::Biscuit;
290    #[test]
291    fn test_create_biscuit() {
292        let subject = "test@test.com".to_owned();
293        let resource: String = "res1".to_string();
294        let operation = "read".to_string();
295        let root = KeyPair::new();
296        let public_key = root.public();
297        let token = create_biscuit(
298            subject.clone(),
299            resource.clone(),
300            operation.clone(),
301            root,
302            TokenTimeConfig::default(),
303        )
304        .unwrap();
305
306        let res = verify_biscuit_local(token, public_key, subject, resource, operation);
307        assert!(res.is_ok());
308    }
309
310    #[test]
311    fn test_biscuit_operations() {
312        let subject = "test@test.com".to_owned();
313        let resource = "res1".to_string();
314        let operation = "read".to_string();
315        let root = KeyPair::new();
316        let public_key = root.public();
317
318        // Test read operation
319        let read_token = create_biscuit(
320            subject.clone(),
321            resource.clone(),
322            operation.clone(),
323            root,
324            TokenTimeConfig::default(),
325        )
326        .unwrap();
327
328        let res = verify_biscuit_local(
329            read_token.clone(),
330            public_key,
331            subject.clone(),
332            resource.clone(),
333            operation.clone(),
334        );
335        assert!(res.is_ok());
336
337        let root = KeyPair::new();
338        let public_key = root.public();
339
340        // Test write operation
341        let write_token = create_biscuit(
342            subject.clone(),
343            resource.clone(),
344            "write".to_string(),
345            root,
346            TokenTimeConfig::default(),
347        )
348        .unwrap();
349
350        let res = verify_biscuit_local(
351            write_token.clone(),
352            public_key,
353            subject.clone(),
354            resource.clone(),
355            "write".to_string(),
356        );
357        assert!(res.is_ok());
358
359        // Test that read token cannot be used for write
360        let res = verify_biscuit_local(
361            read_token,
362            public_key,
363            subject.clone(),
364            resource.clone(),
365            "write".to_string(),
366        );
367        assert!(res.is_err());
368
369        // Test that write token cannot be used for read
370        let res = verify_biscuit_local(
371            write_token,
372            public_key,
373            subject.clone(),
374            resource.clone(),
375            "read".to_string(),
376        );
377        assert!(res.is_err());
378    }
379
380    #[test]
381    fn test_biscuit_expiration() {
382        let subject = "test@test.com".to_owned();
383        let resource = "res1".to_string();
384        let operation = "read".to_string();
385        let root = KeyPair::new();
386        let public_key = root.public();
387        // Create a biscuit with a 5 minute expiration from now
388        let token = create_biscuit(
389            subject.clone(),
390            resource.clone(),
391            operation.clone(),
392            root,
393            TokenTimeConfig::default(),
394        )
395        .unwrap();
396
397        let res = verify_biscuit_local(
398            token.clone(),
399            public_key,
400            subject.clone(),
401            resource.clone(),
402            operation.clone(),
403        );
404        assert!(res.is_ok());
405
406        // Create a biscuit with a start time over 5 minutes ago
407        let root = KeyPair::new();
408        let token = create_biscuit(
409            subject.clone(),
410            resource.clone(),
411            operation.clone(),
412            root,
413            TokenTimeConfig {
414                start_time: Some(Utc::now().timestamp() - 301),
415                duration: 300,
416            },
417        )
418        .unwrap();
419        let res = verify_biscuit_local(token, public_key, subject, resource, operation);
420        assert!(res.is_err());
421    }
422
423    #[test]
424    fn test_custom_token_time_config() {
425        let subject = "test@test.com".to_owned();
426        let resource = "res1".to_string();
427        let operation = "read".to_string();
428        let root = KeyPair::new();
429        let public_key = root.public();
430
431        // Create token with custom start time (1 hour in the past) and longer duration (1 hour)
432        let past_time = Utc::now().timestamp() - 3600;
433        let time_config = TokenTimeConfig {
434            start_time: Some(past_time),
435            duration: 7200, // 2 hours
436        };
437
438        let token = create_biscuit(
439            subject.clone(),
440            resource.clone(),
441            operation.clone(),
442            root,
443            time_config,
444        )
445        .unwrap();
446
447        // Token should be valid at a time between start and expiration
448        let res = verify_biscuit_local(
449            token.clone(),
450            public_key,
451            subject.clone(),
452            resource.clone(),
453            operation.clone(),
454        );
455        assert!(res.is_ok());
456    }
457
458    #[test]
459    fn test_service_chain_biscuit() {
460        let subject = "test@test.com".to_owned();
461        let resource = "res1".to_string();
462        let operation = "read".to_string();
463        let root = KeyPair::new();
464        let public_key = root.public();
465        let chain_key = KeyPair::new();
466        let chain_public_key = hex::encode(chain_key.public().to_bytes());
467        let chain_public_key = format!("ed25519/{}", chain_public_key);
468        let chain_node = ServiceNode {
469            component: "edge_function".to_string(),
470            public_key: chain_public_key.clone(),
471        };
472        let nodes = vec![chain_node];
473        let token = create_service_chain_biscuit(
474            subject.clone(),
475            resource.clone(),
476            operation.clone(),
477            root,
478            &nodes,
479            TokenTimeConfig::default(),
480        );
481        if let Err(e) = &token {
482            println!("Error: {}", e);
483        }
484        assert!(token.is_ok());
485        let token = token.unwrap();
486        let res = verify_biscuit_local(
487            token.clone(),
488            public_key,
489            subject.clone(),
490            resource.clone(),
491            operation.clone(),
492        );
493        assert!(res.is_ok());
494        let biscuit = Biscuit::from(&token, public_key).unwrap();
495        let third_party_request = biscuit.third_party_request().unwrap();
496        let third_party_block = block!(
497            r#"
498            service("res1");
499            "#
500        );
501        let third_party_block = third_party_request
502            .create_block(&chain_key.private(), third_party_block)
503            .unwrap();
504        let attested_biscuit = biscuit
505            .append_third_party(chain_key.public(), third_party_block)
506            .unwrap();
507        let attested_token = attested_biscuit.to_vec().unwrap();
508        let res = verify_service_chain_biscuit_local(
509            attested_token,
510            public_key,
511            subject.clone(),
512            resource.clone(),
513            operation.clone(),
514            nodes,
515            None,
516        );
517        assert!(res.is_ok());
518    }
519
520    #[test]
521    fn test_service_chain_biscuit_with_component_name() {
522        let subject = "test@test.com".to_owned();
523        let resource = "res1".to_string();
524        let root = KeyPair::new();
525        let public_key = root.public();
526
527        // Create two chain nodes
528        let chain_key1 = KeyPair::new();
529        let chain_public_key1 = hex::encode(chain_key1.public().to_bytes());
530        let chain_public_key1 = format!("ed25519/{}", chain_public_key1);
531        let chain_node1 = ServiceNode {
532            component: "edge_function".to_string(),
533            public_key: chain_public_key1.clone(),
534        };
535
536        let chain_key2 = KeyPair::new();
537        let chain_public_key2 = hex::encode(chain_key2.public().to_bytes());
538        let chain_public_key2 = format!("ed25519/{}", chain_public_key2);
539        let chain_node2 = ServiceNode {
540            component: "middleware".to_string(),
541            public_key: chain_public_key2.clone(),
542        };
543
544        let nodes = vec![chain_node1.clone(), chain_node2.clone()];
545
546        // Create the initial token using the first node
547        let token = create_service_chain_biscuit(
548            subject.clone(),
549            resource.clone(),
550            "read".to_string(),
551            root,
552            &nodes,
553            TokenTimeConfig::default(),
554        );
555        assert!(token.is_ok());
556        let token = token.unwrap();
557
558        // Create the biscuit and add third-party blocks
559        let biscuit = Biscuit::from(&token, public_key).unwrap();
560        let third_party_request = biscuit.third_party_request().unwrap();
561        let third_party_block = block!(
562            r#"
563                service("res1");
564            "#
565        );
566        let third_party_block = third_party_request
567            .create_block(&chain_key1.private(), third_party_block)
568            .unwrap();
569        let attested_biscuit = biscuit
570            .append_third_party(chain_key1.public(), third_party_block)
571            .unwrap();
572        let attested_token = attested_biscuit.to_vec().unwrap();
573
574        // Test with the "edge_function" component name - should pass
575        // the first node in the service chain checking itself is valid
576        // since it is checking the base biscuit
577        let res = verify_service_chain_biscuit_local(
578            attested_token.clone(),
579            public_key,
580            subject.clone(),
581            resource.clone(),
582            "read".to_string(),
583            nodes.clone(),
584            Some("edge_function".to_string()),
585        );
586        // This should fail - since we're not verifying any nodes when checking up to but not including "edge_function"
587        assert!(res.is_ok());
588
589        // Create a chain with two nodes
590        let nodes = vec![chain_node1.clone(), chain_node2.clone()];
591
592        // Test with "middleware" component - should succeed verifying node1 only
593        let res = verify_service_chain_biscuit_local(
594            attested_token.clone(),
595            public_key,
596            subject.clone(),
597            resource.clone(),
598            "read".to_string(),
599            nodes.clone(),
600            Some("middleware".to_string()),
601        );
602        assert!(res.is_ok());
603    }
604
605    #[test]
606    fn test_service_chain_biscuit_with_nonexistent_component() {
607        let subject = "test@test.com".to_owned();
608        let resource = "res1".to_string();
609        let root = KeyPair::new();
610        let public_key = root.public();
611        let chain_key = KeyPair::new();
612        let chain_public_key = hex::encode(chain_key.public().to_bytes());
613        let chain_public_key = format!("ed25519/{}", chain_public_key);
614        let chain_node = ServiceNode {
615            component: "edge_function".to_string(),
616            public_key: chain_public_key.clone(),
617        };
618        let nodes = vec![chain_node];
619        let token = create_service_chain_biscuit(
620            subject.clone(),
621            resource.clone(),
622            "read".to_string(),
623            root,
624            &nodes,
625            TokenTimeConfig::default(),
626        );
627        assert!(token.is_ok());
628        let token = token.unwrap();
629
630        let biscuit = Biscuit::from(&token, public_key).unwrap();
631        let third_party_request = biscuit.third_party_request().unwrap();
632        let third_party_block = block!(
633            r#"
634            service("res1");
635            "#
636        );
637        let third_party_block = third_party_request
638            .create_block(&chain_key.private(), third_party_block)
639            .unwrap();
640        let attested_biscuit = biscuit
641            .append_third_party(chain_key.public(), third_party_block)
642            .unwrap();
643        let attested_token = attested_biscuit.to_vec().unwrap();
644
645        // Test with a component name that doesn't exist in the chain
646        let res = verify_service_chain_biscuit_local(
647            attested_token,
648            public_key,
649            subject.clone(),
650            resource.clone(),
651            "read".to_string(),
652            nodes.clone(),
653            Some("nonexistent_component".to_string()),
654        );
655        assert!(res.is_err());
656
657        // Verify the error message contains the component name
658        let err = res.unwrap_err().to_string();
659        assert!(err.contains("nonexistent_component"));
660    }
661
662    #[test]
663    fn test_service_chain_biscuit_with_multiple_nodes() {
664        let subject = "test@test.com".to_owned();
665        let resource = "res1".to_string();
666        let root = KeyPair::new();
667        let public_key = root.public();
668
669        // Create three chain nodes
670        let chain_key1 = KeyPair::new();
671        let chain_public_key1 = hex::encode(chain_key1.public().to_bytes());
672        let chain_public_key1 = format!("ed25519/{}", chain_public_key1);
673        let chain_node1 = ServiceNode {
674            component: "edge_function".to_string(),
675            public_key: chain_public_key1.clone(),
676        };
677
678        let chain_key2 = KeyPair::new();
679        let chain_public_key2 = hex::encode(chain_key2.public().to_bytes());
680        let chain_public_key2 = format!("ed25519/{}", chain_public_key2);
681        let chain_node2 = ServiceNode {
682            component: "middleware".to_string(),
683            public_key: chain_public_key2.clone(),
684        };
685
686        let chain_key3 = KeyPair::new();
687        let chain_public_key3 = hex::encode(chain_key3.public().to_bytes());
688        let chain_public_key3 = format!("ed25519/{}", chain_public_key3);
689        let chain_node3 = ServiceNode {
690            component: "backend".to_string(),
691            public_key: chain_public_key3.clone(),
692        };
693
694        // Create the initial token with the first node
695        let nodes = vec![
696            chain_node1.clone(),
697            chain_node2.clone(),
698            chain_node3.clone(),
699        ];
700        let token = create_service_chain_biscuit(
701            subject.clone(),
702            resource.clone(),
703            "read".to_string(),
704            root,
705            &nodes,
706            TokenTimeConfig::default(),
707        );
708        assert!(token.is_ok());
709        let token = token.unwrap();
710
711        println!("Created initial token");
712
713        // Create the biscuit and add node1's block
714        let biscuit = Biscuit::from(&token, public_key).unwrap();
715        let third_party_request1 = biscuit.third_party_request().unwrap();
716        let third_party_block1 = block!(
717            r#"
718                service("res1");
719            "#
720        );
721        let third_party_block1 = third_party_request1
722            .create_block(&chain_key1.private(), third_party_block1)
723            .unwrap();
724        let attested_biscuit1 = biscuit
725            .append_third_party(chain_key1.public(), third_party_block1)
726            .unwrap();
727
728        // Chain with all three nodes
729        let all_nodes = vec![
730            chain_node1.clone(),
731            chain_node2.clone(),
732            chain_node3.clone(),
733        ];
734        let attested_token1 = attested_biscuit1.to_vec().unwrap();
735
736        // Test 1: Verify up to but not including middleware
737        // This should verify edge_function only
738        let res = verify_service_chain_biscuit_local(
739            attested_token1.clone(),
740            public_key,
741            subject.clone(),
742            resource.clone(),
743            "read".to_string(),
744            all_nodes.clone(),
745            Some("middleware".to_string()),
746        );
747        assert!(res.is_ok());
748
749        // Test 3: Verify up to but not including backend
750        // This should try to verify both edge_function and middleware
751        // but since the middleware attestation wasn't added, it will fail
752        let res = verify_service_chain_biscuit_local(
753            attested_token1.clone(),
754            public_key,
755            subject.clone(),
756            resource.clone(),
757            "read".to_string(),
758            all_nodes.clone(),
759            Some("backend".to_string()),
760        );
761        assert!(res.is_err());
762    }
763
764    #[test]
765    fn test_service_chain_biscuit_with_custom_time() {
766        let subject = "test@test.com".to_owned();
767        let resource = "res1".to_string();
768        let root = KeyPair::new();
769        let public_key = root.public();
770        let chain_key = KeyPair::new();
771        let chain_public_key = hex::encode(chain_key.public().to_bytes());
772        let chain_public_key = format!("ed25519/{}", chain_public_key);
773        let chain_node = ServiceNode {
774            component: "edge_function".to_string(),
775            public_key: chain_public_key.clone(),
776        };
777        let nodes = vec![chain_node];
778
779        // Create a valid token with default time configuration (5 minutes)
780        let valid_token = create_service_chain_biscuit_with_time(
781            subject.clone(),
782            resource.clone(),
783            "read".to_string(),
784            root,
785            &nodes,
786            TokenTimeConfig::default(),
787        );
788        assert!(valid_token.is_ok());
789        let valid_token = valid_token.unwrap().to_vec().unwrap();
790
791        // Verify the valid token works
792        let res = verify_biscuit_local(
793            valid_token.clone(),
794            public_key,
795            subject.clone(),
796            resource.clone(),
797            "read".to_string(),
798        );
799        assert!(res.is_ok());
800
801        // Create an expired token (start time 6 minutes ago with 5 minute duration)
802        let expired_time_config = TokenTimeConfig {
803            start_time: Some(Utc::now().timestamp() - 360), // 6 minutes ago
804            duration: 300,                                  // 5 minutes
805        };
806
807        // Create a new key pair for the expired token
808        let root2 = KeyPair::new();
809        let public_key2 = root2.public();
810
811        let expired_token = create_service_chain_biscuit_with_time(
812            subject.clone(),
813            resource.clone(),
814            "read".to_string(),
815            root2,
816            &nodes,
817            expired_time_config,
818        );
819        assert!(expired_token.is_ok());
820        let expired_token = expired_token.unwrap().to_vec().unwrap();
821
822        // Verify expired token fails
823        let res = verify_biscuit_local(
824            expired_token,
825            public_key2,
826            subject,
827            resource,
828            "read".to_string(),
829        );
830        assert!(res.is_err());
831    }
832}