rust_x402/crypto/
jwt.rs

1//! JWT utilities for authentication
2
3use crate::{Result, X402Error};
4use jsonwebtoken::{Algorithm, Header};
5
6/// JWT claims for Coinbase API authentication
7#[derive(Debug, serde::Serialize)]
8struct Claims {
9    iss: String,
10    sub: String,
11    aud: String,
12    iat: u64,
13    exp: u64,
14    uri: String,
15}
16
17/// JWT options for authentication
18#[derive(Debug, Clone)]
19pub struct JwtOptions {
20    pub key_id: String,
21    pub key_secret: String,
22    pub request_method: String,
23    pub request_host: String,
24    pub request_path: String,
25}
26
27impl JwtOptions {
28    /// Create new JWT options
29    pub fn new(
30        key_id: impl Into<String>,
31        key_secret: impl Into<String>,
32        request_method: impl Into<String>,
33        request_host: impl Into<String>,
34        request_path: impl Into<String>,
35    ) -> Self {
36        Self {
37            key_id: key_id.into(),
38            key_secret: key_secret.into(),
39            request_method: request_method.into(),
40            request_host: request_host.into(),
41            request_path: request_path.into(),
42        }
43    }
44}
45
46/// Generate JWT token for Coinbase API authentication
47pub fn generate_jwt(options: JwtOptions) -> Result<String> {
48    // Remove https:// if present
49    let request_host = options.request_host.trim_start_matches("https://");
50
51    let now = chrono::Utc::now().timestamp() as u64;
52    let exp = now + 300; // 5 minutes
53
54    let claims = Claims {
55        iss: options.key_id.clone(),
56        sub: options.key_id,
57        aud: request_host.to_string(),
58        iat: now,
59        exp,
60        uri: options.request_path,
61    };
62
63    let header = Header::new(Algorithm::HS256);
64    let key = jsonwebtoken::EncodingKey::from_secret(options.key_secret.as_bytes());
65    let token = jsonwebtoken::encode(&header, &claims, &key)
66        .map_err(|e| X402Error::config(format!("JWT encoding failed: {}", e)))?;
67
68    Ok(token)
69}
70
71/// Create an authorization header for Coinbase API requests
72pub fn create_auth_header(
73    api_key_id: &str,
74    api_key_secret: &str,
75    request_host: &str,
76    request_path: &str,
77) -> Result<String> {
78    let options = JwtOptions::new(
79        api_key_id,
80        api_key_secret,
81        "POST", // Default to POST method
82        request_host,
83        request_path,
84    );
85
86    let token = generate_jwt(options)?;
87    Ok(format!("Bearer {}", token))
88}
89
90/// Create auth header with custom method
91pub fn create_auth_header_with_method(
92    api_key_id: &str,
93    api_key_secret: &str,
94    request_method: &str,
95    request_host: &str,
96    request_path: &str,
97) -> Result<String> {
98    let options = JwtOptions::new(
99        api_key_id,
100        api_key_secret,
101        request_method,
102        request_host,
103        request_path,
104    );
105
106    let token = generate_jwt(options)?;
107    Ok(format!("Bearer {}", token))
108}