Hessra Logo
< BACK_TO_DOCS
#identity-tokens#api-keys#bearer-tokens#authentication#migration

Identity Tokens as API Keys

Replace traditional API keys with Hessra identity tokens for automatic expiration, cryptographic security, and seamless upgrade paths to full authorization.

by The Hessra Team

Identity Tokens as API Keys

Hessra identity tokens can now be verified as bearer tokens, making them perfect drop-in replacements for traditional API keys. Get immediate security improvements with automatic expiration and cryptographic signatures, while maintaining a clear upgrade path to full authorization.

> Why Replace API Keys?

Traditional API keys have fundamental problems:

Traditional API KeysHessra Identity Tokens
Static strings in databasesSelf-contained cryptographic tokens
Manual expiration trackingAutomatic expiration built-in
Database lookups for validationOffline verification with public key
No identity informationEmbedded identity for auditing
Complex rotation processesSimple time-based rotation
No delegation capabilityHierarchical delegation support

> Prerequisites

Before implementing identity tokens as API keys, you'll need:

  1. Hessra service access: Join the waitlist or email hello@hessra.net
  2. Service keypair: For signing identity tokens
  3. Public key distribution: Method to share your public key with verifying services

> Installation

Rust SDK

[dependencies]
hessra-sdk = "0.10.0"
hessra-token-identity = "0.2.0"  # For bearer verification

Python SDK

pip install hessra-py

CLI Tool

cargo install hessra

> Basic Implementation

Step 1: Issue Identity Tokens to Customers

Instead of generating random API keys, issue identity tokens:

use hessra_sdk::Hessra;
use hessra_token_core::TokenTimeConfig;

// Your service's Hessra client
let client = Hessra::builder()
    .base_url("auth.yourcompany.com")
    .mtls_cert(&cert)
    .mtls_key(&key)
    .server_ca(&ca)
    .build()?;

// Issue an API key (identity token) for a customer
let identity_response = client
    .request_identity_token(Some("customer:acme-corp"))
    .await?;

// This IS the API key - send to customer
let api_key = identity_response.token;

Using the CLI:

# Issue API key for customer
hessra identity authenticate \
  --server auth.yourcompany.com \
  --cert service.crt \
  --key service.key \
  --ca ca.crt \
  --identity "customer:acme-corp" \
  --ttl 2592000  # 30 days

Step 2: Verify as Bearer Token

Your services verify the token without checking identity:

use hessra_token_identity::{verify_bearer_token, PublicKey};

// Your service's public key (can be distributed freely)
let public_key = PublicKey::from_bytes(&key_bytes)?;

// Verify incoming API key
fn verify_api_key(auth_header: &str) -> Result<()> {
    let token = extract_bearer_token(auth_header)?;

    // Bearer verification: only checks signature and expiration
    verify_bearer_token(token, public_key)?;

    Ok(())
}

Python verification:

from hessra_py import verify_bearer_token, PublicKey

def verify_api_key(auth_header: str):
    token = auth_header.replace("Bearer ", "")
    public_key = PublicKey.from_bytes(key_bytes)

    # Throws exception if invalid or expired
    verify_bearer_token(token, public_key)
    return True

Step 3: Extract Identity for Logging

Even in bearer mode, you can inspect tokens for audit trails:

use hessra_token_identity::{inspect_identity_token, verify_bearer_token};

// First verify as bearer token
verify_bearer_token(&token, public_key)?;

// Then inspect for logging/metrics
let info = inspect_identity_token(&token, public_key)?;
log::info!(
    "API request from: {} (expires: {:?})",
    info.identity,
    info.expiry
);

> Advanced Patterns

JIT (Just-In-Time) Attenuation

Protect long-lived tokens by creating ultra-short-lived versions for network transmission:

use hessra_token_identity::create_short_lived_identity_token;

pub struct SecureAPIClient {
    identity_token: String,  // Long-lived, stays on client
    public_key: PublicKey,
}

impl SecureAPIClient {
    pub async fn make_request(&self, endpoint: &str) -> Result<Response> {
        // Create 5-second token just for this request
        let wire_token = create_short_lived_identity_token(
            self.identity_token.clone(),
            self.public_key
        )?;

        // Only short-lived token goes over network
        let response = reqwest::Client::new()
            .get(endpoint)
            .header("Authorization", format!("Bearer {}", wire_token))
            .send()
            .await?;

        Ok(response)
    }
}

Python SDK integration:

from hessra_py import create_short_lived_identity_token
import requests

class SecureAPIClient:
    def __init__(self, api_key, public_key):
        self.api_key = api_key  # Never sent over network
        self.public_key = public_key

    def request(self, endpoint):
        # Create 5-second token for this request
        wire_token = create_short_lived_identity_token(
            self.api_key,
            self.public_key
        )

        return requests.get(
            endpoint,
            headers={"Authorization": f"Bearer {wire_token}"}
        )

Tiered API Keys

Different customer tiers with different expiration times:

use hessra_token_core::TokenTimeConfig;
use chrono::Duration;

fn issue_api_key(customer_id: &str, tier: &str) -> Result<String> {
    let duration = match tier {
        "free" => Duration::days(7),
        "pro" => Duration::days(30),
        "enterprise" => Duration::days(365),
        _ => Duration::days(30),
    };

    let config = TokenTimeConfig {
        start_time: None,
        duration: duration.num_seconds() as u64,
    };

    let token = create_identity_token(
        format!("customer:{}:tier:{}", customer_id, tier),
        keypair,
        config
    )?;

    Ok(token)
}

Customer Self-Delegation

Enable customers to create sub-keys for their teams:

# Customer delegates to team member
hessra identity delegate \
  --from-token $CUSTOMER_API_KEY \
  --identity "customer:acme-corp:team:dev" \
  --ttl 604800  # 1 week

# Team member delegates to CI/CD
hessra identity delegate \
  --from-token $TEAM_API_KEY \
  --identity "customer:acme-corp:team:dev:ci" \
  --ttl 3600  # 1 hour for CI jobs

> Handling Token Rotation

Automatic Renewal Pattern

Help your customers handle token expiration gracefully:

import time
from datetime import datetime, timedelta

class APIClient:
    def __init__(self, get_new_token_func):
        self.get_new_token = get_new_token_func
        self.token = None
        self.token_expiry = None

    def ensure_valid_token(self):
        # Renew if token expires in less than 7 days
        if not self.token or self.token_expiry < datetime.now() + timedelta(days=7):
            token_response = self.get_new_token()
            self.token = token_response['token']
            self.token_expiry = datetime.fromtimestamp(token_response['expires_at'])

    def make_request(self, endpoint):
        self.ensure_valid_token()
        return requests.get(endpoint, headers={"Authorization": f"Bearer {self.token}"})

Customer Communication

Notify customers before expiration:

# Weekly job to check expiring tokens
for customer in customers_with_tokens:
    days_until_expiry = (customer.token_expiry - datetime.now()).days

    if days_until_expiry == 30:
        send_email(customer, "API key expires in 30 days - renew now")
    elif days_until_expiry == 7:
        send_email(customer, "URGENT: API key expires in 7 days")
    elif days_until_expiry == 1:
        send_email(customer, "CRITICAL: API key expires tomorrow")

> Migration Guide

Parallel Operation

Run both systems while migrating:

fn verify_api_credential(auth_header: &str) -> Result<Identity> {
    if auth_header.starts_with("Bearer hessra_") {
        // New identity token
        let token = extract_token(auth_header)?;
        verify_bearer_token(token, public_key)?;
        let info = inspect_identity_token(token, public_key)?;
        Ok(Identity::from(info.identity))
    } else if auth_header.starts_with("Bearer sk_") {
        // Legacy API key
        let key = extract_token(auth_header)?;
        let identity = lookup_legacy_api_key(&key)?;
        Ok(identity)
    } else {
        Err(Error::InvalidCredential)
    }
}

Migration Steps

  1. Deploy verification support: Update services to accept both token types
  2. Issue new tokens: Start issuing identity tokens to new customers
  3. Notify existing customers: Provide migration timeline and new tokens
  4. Monitor adoption: Track usage of old vs new tokens
  5. Deprecate old system: Remove legacy API key support

Customer Communication Template

## Upgrading to New API Keys

We're upgrading our API key system for better security:

**What's changing:**

- New keys expire automatically (30/60/90 days based on your plan)
- Keys can be delegated to team members
- Better security with cryptographic signatures

**Action required:**

1. Generate your new API key at dashboard.yourcompany.com
2. Update your integration to use the new key
3. Old keys will stop working on [DATE]

**No code changes needed** - new keys work exactly like old ones.

> Configuration Reference

Public Key Distribution

Your public key must be available to services that verify tokens:

// Option 1: Environment variable
let public_key = PublicKey::from_bytes(
    &base64::decode(env::var("HESSRA_PUBLIC_KEY")?)?
)?;

// Option 2: Configuration file
let config = HessraConfig::from_file("hessra.toml")?;
let public_key = config.public_key()?;

// Option 3: Well-known endpoint
let public_key = fetch_public_key("https://auth.yourco.com/.well-known/hessra-public-key").await?;

// Option 4: From Hessra service
let public_key = fetch_public_key("https://yourco.hessra.net/public_key").await?;

Token Configuration

Configure token parameters in your GitOps repository:

# clients.toml
[[clients]]
identifier = { type = "san_uri", value = "uri:urn:service:api" }

# Enable identity tokens for this client
identity_token_enabled = true

# Token duration in seconds
identity_token_duration = 2592000  # 30 days

# Optional: Set shorter duration for specific patterns
[[clients]]
identifier = { type = "san_dns", value = "*.trial.yourco.com" }
identity_token_enabled = true
identity_token_duration = 604800  # 7 days for trial accounts

> Security Considerations

Understanding the Cryptography

Identity tokens are Biscuit tokens - a modern, capability-based token format that uses:

  • Ed25519 or P-256 signatures: Cryptographically secure, cannot be forged
  • Datalog-based authorization logic: Constraints that must ALL be satisfied
  • Attenuation blocks: Each delegation adds restrictions, never removes them
  • Third-party blocks: Cryptographically isolated extensions

This isn't a proprietary format - Biscuit is an open standard with implementations in multiple languages.

Token Expiration Strategy

Use CaseRecommended DurationRationale
Production API Keys30-90 daysBalance security with user convenience
Development Keys7-14 daysHigher risk environment
CI/CD Keys1-24 hoursAutomated renewal possible
JIT Tokens5 secondsNetwork transmission only

Revocation Patterns and Trade-offs

The stateless model means no granular revocation without additional infrastructure. Your options:

  1. Short expiration (Primary approach)

    • Let tokens expire naturally
    • Works well with 30-90 day tokens
    • Zero infrastructure required
  2. Public key rotation (Emergency only)

    • Invalidates ALL tokens immediately
    • Requires redistributing new public key
    • Use only for critical security incidents
  3. Local blocklists (Hybrid approach)

    Biscuit tokens have built-in revocation support with unique identifiers. You can block:

    • Specific tokens by their revocation ID (coming to Hessra SDK)
    • Identities extracted from tokens (available now)
    // Current approach: block by identity
    let blocked_identities = HashSet::from(["customer:compromised"]);
    
    verify_bearer_token(&token, public_key)?;
    let info = inspect_identity_token(&token, public_key)?;
    
    if blocked_identities.contains(&info.identity) {
        return Err("Token revoked");
    }
    
    // Future: Biscuit revocation IDs
    // Each token has a unique revocation_id that can be blocked
    // without affecting other tokens from the same identity
    
  4. Hessra authorization service (Future)

    • Adds stateful revocation capabilities
    • Maintains benefits of offline verification for normal operations
    • Revocation checks only when needed

Private Key Management

Using Hessra Service (Recommended):

  • We handle key generation, storage, and rotation
  • Keys stored in HSM-backed infrastructure
  • Automatic key rotation with zero downtime

Self-Hosting with SDK:

  • Store private keys in HSM or KMS (AWS KMS, Google Cloud KMS, Azure Key Vault)
  • Never store private keys in code or environment variables
  • Implement key rotation strategy (quarterly recommended)
  • Consider using separate keys for different environments

Example with AWS KMS:

use aws_sdk_kms::Client as KmsClient;

async fn sign_with_kms(message: &[u8]) -> Result<Vec<u8>> {
    let kms = KmsClient::new(&aws_config::load_from_env().await);
    let response = kms
        .sign()
        .key_id("arn:aws:kms:region:account:key/id")
        .message(Blob::new(message))
        .signing_algorithm(SigningAlgorithmSpec::EcdsaSha256)
        .send()
        .await?;
    Ok(response.signature.unwrap().into_inner())
}

Performance Considerations

Approximate token verification performance (your results will vary based on hardware and implementation):

OperationRough Order of MagnitudeNotes
Ed25519 signature verificationSub-millisecondGenerally faster than P-256
P-256 signature verificationSub-millisecondNIST standard, slightly slower
Database query (comparison)1-10msIncludes network round-trip
In-memory string comparisonMicrosecondsBut requires state management

Important: These are rough estimates. Actual performance depends on:

  • CPU architecture and speed
  • Implementation quality (language, libraries)
  • Network latency (for database queries)
  • Load and caching strategies

We recommend benchmarking in your specific environment. For most applications, the cryptographic overhead is negligible compared to network I/O.

For high-throughput services:

  • Cache verification results for repeated tokens (with TTL)
  • Use Ed25519 over P-256 for better performance
  • Consider connection pooling for Hessra service calls
  • Benchmark with your actual workload

Attenuation Security Model

Delegation can only narrow scope, never expand:

// Original token for "customer:acme"
let original = create_identity_token("customer:acme", keypair, config)?;

// ✅ Valid: narrowing to sub-identity
let valid = attenuate_identity_token(
    &original,
    "customer:acme:team:dev",  // More specific
    public_key
)?;

// ❌ Invalid: would be rejected by the token logic
let invalid = attenuate_identity_token(
    &original,
    "customer:bigcorp",  // Different customer - not allowed
    public_key
)?;

// ❌ Invalid: would be rejected by the token logic
let invalid = attenuate_identity_token(
    &original,
    "admin:superuser",  // Privilege escalation - not allowed
    public_key
)?;

Replay Attack Mitigation

While tokens are time-bound, they can be replayed within their validity window:

For idempotent operations (GET, PUT):

  • Token replay is generally safe
  • Use standard HTTP caching headers

For non-idempotent operations (POST, financial transactions):

  • Add request-specific nonces
  • Use request signing in addition to bearer tokens
  • Consider shorter token expiration (seconds, not minutes)

Example with nonce:

struct AuthorizedRequest {
    token: String,
    nonce: String,  // UUID or timestamp+random
    signature: String,  // Sign(token + nonce + body)
}

Best Practices

  1. Always use HTTPS: Even with signed tokens
  2. Implement rate limiting: Tokens don't prevent abuse
  3. Log token usage: Track identity for audit trails
  4. Monitor expiration: Alert customers before tokens expire
  5. Use JIT attenuation: When possible, keep long-lived tokens off the network

> Upgrade Path to Full Authorization

Identity tokens as API keys are just the beginning:

Today: Bearer Tokens

  • Simple API key replacement
  • Automatic expiration
  • Identity tracking

Tomorrow: JIT Attenuation

  • SDK integration
  • 5-second network tokens
  • Original tokens stay secure

Next Week: Scoped Authorization

  • Exchange identity for authorization tokens
  • Fine-grained permissions
  • Single-action tokens

Next Month: Full Delegation

  • Customer self-service
  • Team management
  • Hierarchical access control

> Quick Reference

CLI Commands

# Issue identity token (API key)
hessra identity authenticate --identity "customer:id"

# Verify token
hessra identity verify --token $TOKEN

# Delegate token
hessra identity delegate --from-token $TOKEN --identity "sub:identity"

# Inspect token
hessra identity inspect --token $TOKEN

SDK Functions

// Rust
use hessra_token_identity::{
    verify_bearer_token,           // Bearer verification
    verify_identity_token,          // Full identity verification
    inspect_identity_token,         // Extract identity info
    create_short_lived_identity_token, // JIT attenuation
};

// Python
from hessra_py import (
    verify_bearer_token,            # Bearer verification
    verify_identity_token,          # Full identity verification
    inspect_identity_token,         # Extract identity info
    create_short_lived_identity_token, # JIT attenuation
)

> Getting Help


Start with bearer tokens today. Grow into full authorization tomorrow. No migration required.