HeliosDB JWT Authentication Configuration Guide
HeliosDB JWT Authentication Configuration Guide
Overview
HeliosDB uses industry-standard JWT (JSON Web Tokens) for API authentication across all REST, GraphQL, and HTTP Gateway endpoints. This guide provides comprehensive configuration instructions for development, staging, and production environments.
Table of Contents
- Quick Start
- JWT Architecture
- Configuration
- Token Lifecycle
- Security Best Practices
- API Integration
- Troubleshooting
- Advanced Topics
Quick Start
Generate and Validate a Token
use heliosdb_security::jwt::{JwtValidator, JwtConfig};use std::collections::HashMap;
#[tokio::main]async fn main() -> anyhow::Result<()> { // Create validator with default configuration let validator = JwtValidator::new(JwtConfig::default())?;
// Issue a token let token = validator.issue_token( "user123", // User ID vec!["read".to_string()], // Permissions HashMap::new(), // Metadata )?;
println!("Token: {}", token);
// Validate the token let claims = validator.validate(&token).await?; println!("User: {}", claims.sub); println!("Permissions: {:?}", claims.permissions);
Ok(())}Using JWT in HTTP Requests
# Obtain a token (implementation-specific)export TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9..."
# Make authenticated requestcurl -H "Authorization: Bearer $TOKEN" \ https://heliosdb.example.com/snowflake/queriesJWT Architecture
Token Structure
HeliosDB JWTs conform to RFC 7519 and consist of three parts:
HEADER.PAYLOAD.SIGNATUREExample Token:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYzEyMyJ9.eyJzdWIiOiJ1c2VyMTIzIiwiZXhwIjoxNzMxMzUwNDAwLCJpYXQiOjE3MzEzNDY4MDAsIm5iZiI6MTczMTM0NjgwMCwiYXVkIjoiaGVsaW9zZGItYXBpIiwiaXNzIjoiaGVsaW9zZGIiLCJqdGkiOiI1NTBlOTcyNi1hNGQ4LTQyZGQtYTQ0MC00NWJhOTY3OWE2ZmUiLCJwZXJtaXNzaW9ucyI6WyJyZWFkIiwid3JpdGUiXSwic2Vzc2lvbl9pZCI6IjdhMmJjZDEyLTM0NTYtNzg5MC1hYmNkLWVmMTIzNDU2Nzg5MCIsIm1ldGFkYXRhIjp7fX0.[RS256_SIGNATURE]Decoded Structure
Header:
{ "alg": "RS256", // Algorithm "typ": "JWT", // Type "kid": "abc123" // Key ID for rotation}Payload (Claims):
{ "sub": "user123", // Subject (user ID) "exp": 1731350400, // Expiration time "iat": 1731346800, // Issued at "nbf": 1731346800, // Not before "aud": "heliosdb-api", // Audience "iss": "heliosdb", // Issuer "jti": "550e9726-a4d8-42dd-a440-45ba9679a6fe", // JWT ID (unique) "permissions": ["read", "write"], // Custom permissions "session_id": "7a2bcd12-3456-7890-abcd-...", // Session tracking "metadata": { // Custom metadata "department": "engineering", "role": "senior" }}Signature
The signature is computed using RS256 (RSA-SHA256) by default:
RSASSA-PKCS1-v1_5( SHA256(base64url(header) + "." + base64url(payload)), private_key)Configuration
Default Configuration
use heliosdb_security::jwt::JwtConfig;use chrono::Duration;use jsonwebtoken::Algorithm;
let config = JwtConfig { // Required issuer (validates 'iss' claim) issuer: Some("heliosdb".to_string()),
// Required audience (validates 'aud' claim) audience: Some("heliosdb-api".to_string()),
// Allowed signature algorithms algorithms: vec![Algorithm::RS256],
// Clock skew tolerance (seconds) leeway: 60,
// Access token lifetime token_lifetime: Duration::hours(1),
// Refresh token lifetime refresh_lifetime: Duration::days(7),
// Enable rate limiting rate_limit_enabled: true,
// Max validations per second rate_limit_per_second: 100,};Environment-Specific Configuration
Development
let dev_config = JwtConfig { issuer: Some("heliosdb-dev".to_string()), audience: Some("heliosdb-api-dev".to_string()), algorithms: vec![Algorithm::RS256, Algorithm::ES256], leeway: 120, // More lenient token_lifetime: Duration::hours(24), // Long-lived for testing refresh_lifetime: Duration::days(30), rate_limit_enabled: false, // Disabled for testing rate_limit_per_second: 1000,};Staging
let staging_config = JwtConfig { issuer: Some("heliosdb-staging".to_string()), audience: Some("heliosdb-api-staging".to_string()), algorithms: vec![Algorithm::RS256], leeway: 60, token_lifetime: Duration::hours(1), refresh_lifetime: Duration::days(7), rate_limit_enabled: true, rate_limit_per_second: 100,};Production
let prod_config = JwtConfig { issuer: Some("heliosdb-production".to_string()), audience: Some("heliosdb-api".to_string()), algorithms: vec![Algorithm::RS256], // Only RS256 leeway: 30, // Strict clock tolerance token_lifetime: Duration::minutes(15), // Short-lived refresh_lifetime: Duration::days(1), // Short refresh window rate_limit_enabled: true, rate_limit_per_second: 50, // Strict rate limiting};Configuration from Environment Variables
use std::env;use chrono::Duration;
fn load_jwt_config() -> anyhow::Result<JwtConfig> { Ok(JwtConfig { issuer: env::var("JWT_ISSUER").ok(), audience: env::var("JWT_AUDIENCE").ok(), algorithms: vec![Algorithm::RS256], leeway: env::var("JWT_LEEWAY") .ok() .and_then(|v| v.parse().ok()) .unwrap_or(60), token_lifetime: Duration::seconds( env::var("JWT_TOKEN_LIFETIME_SECS") .ok() .and_then(|v| v.parse().ok()) .unwrap_or(3600) ), refresh_lifetime: Duration::seconds( env::var("JWT_REFRESH_LIFETIME_SECS") .ok() .and_then(|v| v.parse().ok()) .unwrap_or(604800) ), rate_limit_enabled: env::var("JWT_RATE_LIMIT_ENABLED") .ok() .and_then(|v| v.parse().ok()) .unwrap_or(true), rate_limit_per_second: env::var("JWT_RATE_LIMIT_PER_SEC") .ok() .and_then(|v| v.parse().ok()) .unwrap_or(100), })}Environment Variables:
# .env fileJWT_ISSUER=heliosdb-productionJWT_AUDIENCE=heliosdb-apiJWT_LEEWAY=30JWT_TOKEN_LIFETIME_SECS=900 # 15 minutesJWT_REFRESH_LIFETIME_SECS=86400 # 1 dayJWT_RATE_LIMIT_ENABLED=trueJWT_RATE_LIMIT_PER_SEC=50Token Lifecycle
1. Token Issuance
use heliosdb_security::jwt::JwtValidator;use std::collections::HashMap;
let validator = JwtValidator::new(config)?;
// Issue access tokenlet access_token = validator.issue_token( "user123", vec!["read".to_string(), "write".to_string()], { let mut metadata = HashMap::new(); metadata.insert("department".to_string(), "engineering".to_string()); metadata },)?;
// Issue refresh tokenlet refresh_token = validator.issue_refresh_token("user123")?;2. Token Validation
// Validate token (async)let claims = validator.validate(&access_token).await?;
// Synchronous validation (for non-async contexts)let claims = validator.validate_sync(&access_token)?;
// Extract user informationprintln!("User ID: {}", claims.sub);println!("Permissions: {:?}", claims.permissions);println!("Session ID: {:?}", claims.session_id);3. Token Refresh
// Use refresh token to get new access tokenlet new_access_token = validator.refresh_token(&refresh_token).await?;
// Refresh token contains special "refresh" permission// and is only valid for refreshing, not API access4. Token Revocation
// Revoke a token (adds to revocation list)validator.revoke_token(&access_token).await?;
// Token will fail validation after revocationmatch validator.validate(&access_token).await { Err(e) if e.to_string().contains("revoked") => { println!("Token has been revoked"); } _ => {}}5. Revocation Cleanup
// Clean up expired revoked tokens (periodic maintenance)let removed = validator.cleanup_revocations().await?;println!("Cleaned up {} expired revocations", removed);Security Best Practices
1. Key Management
Generate RSA Keys
use heliosdb_security::jwt::JwtKeyPair;
// Generate new RSA key pair (2048-bit)let key_pair = JwtKeyPair::generate_rsa()?;
// Export for storageprintln!("Key ID: {}", key_pair.kid);println!("Algorithm: {:?}", key_pair.algorithm);Load Keys from PEM
use std::fs;
let private_key_pem = fs::read("/path/to/private_key.pem")?;let key_pair = JwtKeyPair::from_rsa_pem(&private_key_pem)?;Key Rotation
// Generate new keylet new_key = JwtKeyPair::generate_rsa()?;
// Add to validator (old keys remain valid)validator.add_key(new_key.clone())?;
// Switch to new key for signing (rotation complete)validator.rotate_key(new_key)?;
// Clean up old keys (keep last 3)let removed = validator.cleanup_old_keys(3)?;println!("Removed {} old keys", removed);2. Token Storage
DO:
- Store access tokens in memory (JavaScript variables)
- Use httpOnly cookies for refresh tokens
- Clear tokens on logout
- Implement token refresh logic
DON’T:
- ❌ Store tokens in localStorage (XSS risk)
- ❌ Include tokens in URLs (logs/history exposure)
- ❌ Send tokens over unencrypted connections
- ❌ Share tokens between users
3. Token Transmission
Always use HTTPS:
# ❌ INSECUREcurl -H "Authorization: Bearer $TOKEN" http://api.example.com/data
# SECUREcurl -H "Authorization: Bearer $TOKEN" https://api.example.com/data4. Rate Limiting
// Enable rate limiting in productionlet config = JwtConfig { rate_limit_enabled: true, rate_limit_per_second: 50, // Adjust based on load ..Default::default()};5. Monitoring
// Track validation attemptslet validation_result = validator.validate(&token).await;
match validation_result { Ok(claims) => { // Log successful authentication tracing::info!( user_id = %claims.sub, jti = ?claims.jti, "Token validated successfully" ); } Err(e) => { // Log failed authentication tracing::warn!( error = %e, token_preview = &token[..20], "Token validation failed" ); }}API Integration
REST API (Axum)
use axum::{ Router, routing::get, middleware,};use heliosdb_rest::auth::{require_auth, HasAuthConfig};
let app = Router::new() .route("/api/protected", get(protected_handler)) .layer(middleware::from_fn_with_state( app_state.clone(), require_auth, )) .with_state(app_state);GraphQL
use heliosdb_graphql::auth::{TokenManager, Claims};
// In GraphQL contextpub struct Context { token_manager: TokenManager, claims: Option<Claims>,}
// In resolverfn resolve_user(&self, ctx: &Context) -> Result<User> { let claims = ctx.claims.as_ref() .ok_or("Not authenticated")?;
if !claims.has_role("admin") { return Err("Insufficient permissions".into()); }
// ... rest of resolver}HTTP Gateway
use heliosdb_protocols::http_gateway::HttpGateway;
let gateway = HttpGateway::new()?;
// JWT validator is automatically used for authentication// All Snowflake/Databricks/Pinecone endpoints require valid JWTProtocol Handlers
use heliosdb_protocols::auth::{JwtValidator, JwtConfig};
// Create validator for protocol handlerlet jwt_validator = JwtValidator::new(JwtConfig::default())?;
// Validate tokens in custom handlersasync fn handle_request(token: &str) -> Result<()> { let claims = jwt_validator.validate(token).await?;
// Check permissions if !claims.permissions.contains(&"admin".to_string()) { return Err("Forbidden".into()); }
Ok(())}Troubleshooting
Common Errors
1. “Token has expired”
Cause: Token exp claim is in the past
Solution:
// Increase token lifetime (development only)config.token_lifetime = Duration::hours(24);
// Production: Implement token refreshlet new_token = validator.refresh_token(&refresh_token).await?;2. “Invalid token: Invalid signature”
Cause: Token signature doesn’t match, or wrong key used
Solutions:
- Verify same key is used for signing and validation
- Check if key rotation occurred
- Ensure token wasn’t tampered with
3. “Rate limit exceeded”
Cause: Too many validation requests
Solutions:
// Increase rate limit (if legitimate load)config.rate_limit_per_second = 200;
// Or disable for testingconfig.rate_limit_enabled = false;4. “Token has been revoked”
Cause: Token was explicitly revoked
Solution:
// Get new tokenlet new_token = validator.issue_token(user_id, permissions, metadata)?;5. “Missing authorization header”
Cause: No Authorization header in request
Solution:
# Always include Bearer tokencurl -H "Authorization: Bearer $TOKEN" https://api.example.com/endpointDebugging Tools
Inspect Token
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
fn decode_jwt_payload(token: &str) -> anyhow::Result<serde_json::Value> { let parts: Vec<&str> = token.split('.').collect(); if parts.len() != 3 { anyhow::bail!("Invalid JWT format"); }
let payload = URL_SAFE_NO_PAD.decode(parts[1])?; Ok(serde_json::from_slice(&payload)?)}Check Token Expiration
let payload = decode_jwt_payload(&token)?;let exp = payload["exp"].as_i64().unwrap();let now = chrono::Utc::now().timestamp();
println!("Token expires at: {}", exp);println!("Current time: {}", now);println!("Time remaining: {} seconds", exp - now);Validate Without Rate Limiting
// Temporarily disable rate limiting for debugginglet mut debug_config = config.clone();debug_config.rate_limit_enabled = false;
let debug_validator = JwtValidator::new(debug_config)?;let claims = debug_validator.validate(&token).await?;Advanced Topics
1. Custom Claims
use heliosdb_security::jwt::JwtClaims;use chrono::Utc;use uuid::Uuid;
let custom_claims = JwtClaims { sub: "user123".to_string(), exp: (Utc::now() + chrono::Duration::hours(1)).timestamp(), iat: Utc::now().timestamp(), nbf: Some(Utc::now().timestamp()), aud: Some("heliosdb-api".to_string()), iss: Some("heliosdb".to_string()), jti: Some(Uuid::new_v4().to_string()), permissions: vec!["admin".to_string()], session_id: Some("session_123".to_string()), metadata: { let mut meta = HashMap::new(); meta.insert("tenant_id".to_string(), "tenant_456".to_string()); meta.insert("ip_address".to_string(), "192.168.1.1".to_string()); meta },};
let token = validator.create_token(&custom_claims)?;2. JWKS (JSON Web Key Set) Endpoint
use axum::{routing::get, Json};
async fn jwks_endpoint( State(validator): State<Arc<JwtValidator>>,) -> Json<serde_json::Value> { let jwks = validator.get_jwks().unwrap(); Json(jwks)}
// Mount endpointlet app = Router::new() .route("/.well-known/jwks.json", get(jwks_endpoint));Response:
{ "keys": [ { "kid": "abc123", "alg": "RS256", "use": "sig", "kty": "RSA" } ]}3. Redis-Based Revocation
use heliosdb_security::jwt::RedisRevocationList;
#[cfg(feature = "redis-revocation")]{ let revocation_list = Arc::new( RedisRevocationList::new( "redis://localhost:6379", "heliosdb".to_string(), ).await? );
let validator = JwtValidator::with_revocation_list( config, revocation_list, )?;}4. Multi-Tenancy
// Include tenant ID in metadatalet mut metadata = HashMap::new();metadata.insert("tenant_id".to_string(), tenant_id.clone());
let token = validator.issue_token(user_id, permissions, metadata)?;
// Validate tenant isolation in handlerasync fn handle_request(claims: &JwtClaims, resource: &Resource) -> Result<()> { let tenant_id = claims.metadata.get("tenant_id") .ok_or("Missing tenant ID")?;
if resource.tenant_id != tenant_id { return Err("Tenant isolation violation".into()); }
Ok(())}5. Performance Optimization
// Use connection pooling for validationuse dashmap::DashMap;use std::sync::Arc;
struct TokenCache { cache: Arc<DashMap<String, (JwtClaims, i64)>>,}
impl TokenCache { fn get(&self, token: &str) -> Option<JwtClaims> { self.cache.get(token).and_then(|entry| { let (claims, exp) = entry.value(); if *exp > chrono::Utc::now().timestamp() { Some(claims.clone()) } else { None } }) }
fn set(&self, token: String, claims: JwtClaims) { self.cache.insert(token, (claims.clone(), claims.exp)); }}Reference
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
issuer | Option<String> | Some("heliosdb") | Required token issuer |
audience | Option<String> | Some("heliosdb-api") | Required token audience |
algorithms | Vec<Algorithm> | [RS256] | Allowed signature algorithms |
leeway | u64 | 60 | Clock skew tolerance (seconds) |
token_lifetime | Duration | 1 hour | Access token lifetime |
refresh_lifetime | Duration | 7 days | Refresh token lifetime |
rate_limit_enabled | bool | true | Enable rate limiting |
rate_limit_per_second | u32 | 100 | Max validations per second |
JWT Claims
| Claim | Type | Required | Description |
|---|---|---|---|
sub | String | Yes | Subject (user ID) |
exp | i64 | Yes | Expiration time (Unix timestamp) |
iat | i64 | Yes | Issued at (Unix timestamp) |
nbf | Option<i64> | No | Not before (Unix timestamp) |
aud | Option<String> | No | Audience |
iss | Option<String> | No | Issuer |
jti | Option<String> | No | JWT ID (required for revocation) |
permissions | Vec<String> | No | Custom permissions |
session_id | Option<String> | No | Session tracking ID |
metadata | HashMap<String, String> | No | Custom key-value metadata |
Algorithms
| Algorithm | Security | Key Type | Recommended |
|---|---|---|---|
| RS256 | High | RSA 2048-bit | Yes (default) |
| RS384 | High | RSA 3072-bit | Yes |
| RS512 | High | RSA 4096-bit | Yes |
| ES256 | High | ECDSA P-256 | Yes |
| ES384 | High | ECDSA P-384 | Yes |
| HS256 | Medium | Symmetric | ⚠ Dev only |
| HS384 | Medium | Symmetric | ⚠ Dev only |
| HS512 | Medium | Symmetric | ⚠ Dev only |
Recommendation: Use RS256 for production deployments.
Support
For issues or questions:
- GitHub Issues: https://github.com/heliosdb/heliosdb/issues
- Documentation: https://docs.heliosdb.com/security/authentication
- Security: security@heliosdb.com (for vulnerabilities)
Version: 7.0.0 Last Updated: 2025-11-10