hessra_token/
lib.rs

1//! # Hessra Token
2//!
3//! Core verification library for Hessra authentication tokens.
4//!
5//! This crate provides functionality for verifying and attenuating biscuit tokens
6//! used in the Hessra authentication system. It is designed to be WASM-compatible
7//! and has no networking dependencies.
8//!
9//! ## Features
10//!
11//! - Token verification: Verify tokens without contacting the authorization server
12//! - Token attestation: Add service node attestations to tokens
13//! - WASM compatibility: Can be compiled to WebAssembly for use in browsers
14//!
15//! ## Usage
16//!
17//! ```no_run
18//! use hessra_token::{verify_token, biscuit_key_from_string};
19//!
20//! fn main() -> Result<(), hessra_token::TokenError> {
21//!     let token_base64 = "YOUR_TOKEN_STRING";
22//!     
23//!     // Parse public key from string format
24//!     let public_key = biscuit_key_from_string("ed25519/01234567890abcdef".to_string())?;
25//!     
26//!     // Verify the token
27//!     verify_token(token_base64, public_key, "user123", "resource456")?;
28//!     
29//!     println!("Token verification successful!");
30//!     Ok(())
31//! }
32//! ```
33
34mod attenuate;
35mod error;
36mod token;
37mod utils;
38mod verify;
39
40pub use attenuate::add_service_node_attenuation;
41pub use error::TokenError;
42pub use token::{parse_token, verify_service_chain_token, verify_token};
43pub use utils::{decode_token, encode_token, public_key_from_pem_file};
44pub use verify::{
45    biscuit_key_from_string, verify_biscuit_local, verify_service_chain_biscuit_local, ServiceNode,
46};
47
48// Re-export biscuit types that are needed for public API
49pub use biscuit_auth::{Biscuit, KeyPair, PublicKey};
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54    use biscuit_auth::macros::biscuit;
55
56    #[test]
57    fn test_verify_biscuit_local() {
58        // Create a test keypair
59        let keypair = KeyPair::new();
60        let public_key = keypair.public();
61
62        // Create a simple test biscuit
63        let biscuit_builder = biscuit!(
64            r#"
65                right("alice", "resource1", "read");
66                right("alice", "resource1", "write");
67            "#
68        );
69        let biscuit = biscuit_builder.build(&keypair).unwrap();
70        let token_bytes = biscuit.to_vec().unwrap();
71
72        // Verify the biscuit
73        let result = verify_biscuit_local(
74            token_bytes,
75            public_key,
76            "alice".to_string(),
77            "resource1".to_string(),
78        );
79        assert!(result.is_ok());
80    }
81
82    #[test]
83    fn test_verify_service_chain_biscuit() {
84        // Create test keypairs
85        let root_keypair = KeyPair::new();
86        let service_keypair = KeyPair::new();
87        let service_public_key_hex = hex::encode(service_keypair.public().to_bytes());
88        let service_public_key_str = format!("ed25519/{}", service_public_key_hex);
89
90        // Create a simple test biscuit with separate node facts
91        let biscuit_builder = biscuit!(
92            r#"
93                right("alice", "resource1", "read");
94                right("alice", "resource1", "write");
95                node("resource1", "service1");
96            "#
97        );
98        let biscuit = biscuit_builder.build(&root_keypair).unwrap();
99        let token_bytes = biscuit.to_vec().unwrap();
100
101        // Define service nodes
102        let service_nodes = vec![ServiceNode {
103            component: "service1".to_string(),
104            public_key: service_public_key_str,
105        }];
106
107        // Verify the biscuit with service chain
108        let result = verify_service_chain_biscuit_local(
109            token_bytes,
110            root_keypair.public(),
111            "alice".to_string(),
112            "resource1".to_string(),
113            service_nodes,
114            None,
115        );
116        assert!(result.is_ok());
117    }
118
119    #[test]
120    fn test_add_service_node_attenuation() {
121        // Create test keypairs
122        let root_keypair = KeyPair::new();
123        let service_keypair = KeyPair::new();
124
125        // Create a simple test biscuit
126        let biscuit_builder = biscuit!(
127            r#"
128                right("alice", "resource1", "read");
129                right("alice", "resource1", "write");
130            "#
131        );
132        let biscuit = biscuit_builder.build(&root_keypair).unwrap();
133        let token_bytes = biscuit.to_vec().unwrap();
134
135        // Add service node attenuation
136        let attenuated_token = add_service_node_attenuation(
137            token_bytes,
138            root_keypair.public(),
139            "resource1",
140            &service_keypair,
141        );
142        assert!(attenuated_token.is_ok());
143
144        // Verify the biscuit still works
145        let result = verify_biscuit_local(
146            attenuated_token.unwrap(),
147            root_keypair.public(),
148            "alice".to_string(),
149            "resource1".to_string(),
150        );
151        assert!(result.is_ok());
152    }
153
154    #[test]
155    fn test_base64_utils() {
156        // Create a test keypair and biscuit
157        let keypair = KeyPair::new();
158        let biscuit_builder = biscuit!(
159            r#"
160                right("alice", "resource1", "read");
161            "#
162        );
163        let biscuit = biscuit_builder.build(&keypair).unwrap();
164        let original_bytes = biscuit.to_vec().unwrap();
165
166        // Test encoding
167        let encoded = encode_token(&original_bytes);
168        assert!(!encoded.is_empty());
169
170        // Test decoding
171        let decoded = decode_token(&encoded).unwrap();
172        assert_eq!(original_bytes, decoded);
173
174        // Test decoding with invalid input
175        let result = decode_token("invalid-base64!");
176        assert!(result.is_err());
177    }
178
179    #[test]
180    fn test_verify_token_string() {
181        // Create a test keypair and biscuit
182        let keypair = KeyPair::new();
183        let biscuit_builder = biscuit!(
184            r#"
185                right("alice", "resource1", "read");
186                right("alice", "resource1", "write");
187            "#
188        );
189        let biscuit = biscuit_builder.build(&keypair).unwrap();
190        let token_bytes = biscuit.to_vec().unwrap();
191        let token_string = encode_token(&token_bytes);
192
193        // Test verify_token
194        let result = verify_token(&token_string, keypair.public(), "alice", "resource1");
195        assert!(result.is_ok());
196
197        // Test with invalid subject
198        let result = verify_token(&token_string, keypair.public(), "bob", "resource1");
199        assert!(result.is_err());
200    }
201}