Hessra Logo
< BACK_TO_BLOG
#identity-tokens#api-keys#bearer-tokens#security#authentication#saas

How to Implement API Keys Using Hessra Identity Tokens

Replace static API keys with cryptographically secure, auto-expiring identity tokens that provide a seamless upgrade path to full authorization.

by Jacob Valentic

How to Implement API Keys Using Hessra Identity Tokens

API keys are everywhere. They're simple, they work, and developers understand them. But they're also a security nightmare: long-lived credentials sitting in databases, environment variables, and (let's be honest) accidentally committed to git repos.

What if your API keys could expire automatically without backend tracking? What if they could identify exactly who's using them? What if they could grow into a full authorization system as your security needs mature?

Hessra identity tokens now support bearer token verification, making them a drop-in replacement for traditional API keys with immediate security wins and a clear upgrade path.

> What is Hessra?

Hessra is a managed service for cryptographic authorization, but you have options:

  • Use the Hessra service: We handle token minting, key management, and provide a simple API
  • Use our open-source SDKs: Implement your own token operations (verification, delegation, minting) with our libraries

Identity tokens are actually Biscuit tokens, an established standard for capability-based security with Ed25519 or P-256 signatures. Hessra provides an opinionated implementation optimized for API key replacement, machine identities, and authorization. Learn more in our introduction post.

> The Problem with Traditional API Keys

Traditional API keys are usually just random strings. To make them useful, you need to:

  • Store their hash in a database (another thing to protect)
  • Track their state (active, revoked, expired)
  • Build rotation mechanisms (that nobody uses)
  • Hope they don't leak (they will)

When they do leak, you're scrambling to revoke them, audit their usage, and figure out what damage was done. And if you want to add expiration? That's more backend logic to maintain.

> Identity Tokens as Bearer Tokens

Hessra identity tokens can now be verified as bearer tokens, checking only cryptographic validity and expiration. This makes them perfect API key replacements that are:

  • Self-contained: Expiration is encoded in the token itself
  • Cryptographically signed: Can't be forged or tampered with
  • Zero backend state: No database tracking needed
  • Automatically expiring: Built-in time limits, no cron jobs required

Here's how simple it is to verify one:

use hessra_token_identity::{verify_bearer_token, inspect_identity_token, PublicKey};

// Your service's public key (distributed to services that need to verify)
let public_key = PublicKey::from_bytes(&key_bytes)?;

// Verify and extract identity in one go
match verify_bearer_token(api_token.clone(), public_key) {
    Ok(_) => {
        // Token is valid - now get the identity for your logic
        let info = inspect_identity_token(api_token, public_key)?;
        println!("API call from: {}", info.identity);
        // Use info.identity in your existing auth logic
    }
    Err(e) => {
        // Token is invalid, expired, or tampered
        println!("Invalid API key: {e}");
    }
}

Performance Note: Cryptographic verification takes ~0.1-0.5ms on modern hardware - comparable to a database query but without the network hop. See our documentation for benchmarks.

> Implementation Guide

Step 1: Issue Identity Tokens as API Keys

Instead of generating random strings, issue identity tokens to your customers:

use hessra_sdk::Hessra;
use chrono::{Duration, Utc};

// Option A: Use Hessra service (we handle the private keys)
let client = Hessra::builder()
    .base_url("yourco.hessra.net")  // Your Hessra instance
    .mtls_cert(&cert) // your service's mTLS identity
    .mtls_key(&key)
    .server_ca(&ca)
    .build()?;

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

// Option B: DIY with the SDK (you manage keypairs)
use hessra_token_identity::create_identity_token;
use hessra_token_core::{KeyPair, TokenTimeConfig};

let keypair = KeyPair::new(); // Or load from HSM/KMS
let token = create_identity_token(
    "customer:acme-corp".to_string(),
    keypair,
    TokenTimeConfig::default()
)?;

// This IS the API key - no database storage needed
let api_key = identity_response.token;

The customer uses it exactly like a traditional API key:

curl -H "Authorization: Bearer $API_KEY" \
  https://api.yourservice.com/v1/data

Step 2: Extract Identity Without Full Verification

Even in bearer mode, you can inspect the token to see who's using it:

use hessra_token_identity::{inspect_identity_token, verify_bearer_token};

// First, verify it's valid (bearer mode)
verify_bearer_token(&token, public_key)?;

// Then inspect it for logging/metrics or to use in your existing simple auth logic
let info = inspect_identity_token(&token, public_key)?;
println!("API call from: {}", info.identity);
println!("Token expires at: {:?}", info.expiry);

This gives you usage analytics without the overhead of full identity verification. Or, more likely, lets you use your existing in-code identity and authorization logic for your API.

> The Upgrade Path: From API Keys to Full Authorization

The beauty of starting with identity tokens as API keys is the natural upgrade path to more sophisticated security:

Level 1: Basic Bearer Token (Today)

Customer gets a token, uses it as an API key:

import requests

response = requests.get(
    "https://api.yourservice.com/data",
    headers={"Authorization": f"Bearer {api_key}"}
)

Your service verifies it as a bearer token. Simple, secure, stateless.

Level 2: JIT Attenuation in Your SDK (Tomorrow)

Add the Hessra SDK to your own SDK/API library to create just-in-time attenuated tokens. The original identity token stays secure on the client, and only ultra-short-lived versions travel over the network:

use hessra_token_identity::create_short_lived_identity_token;

// Your SDK stores the long-lived identity token
pub struct YourAPIClient {
    identity_token: String,
    public_key: PublicKey,
}

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

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

        Ok(response)
    }
}

Or in Python:

from hessra_py import create_short_lived_identity_token
import requests

class YourAPIClient:
    def __init__(self, identity_token, public_key):
        self.identity_token = identity_token  # Stays on client
        self.public_key = public_key

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

        # Only the short-lived token is sent
        response = requests.get(
            endpoint,
            headers={"Authorization": f"Bearer {wire_token}"}
        )
        return response

This gives you:

  • Zero network exposure: The real API key (identity token) never leaves the client
  • 5-second window: Even if intercepted, tokens expire almost immediately
  • No server changes: Your backend still verifies bearer tokens the same way
  • Transparent to users: They just provide their API key once to your SDK

The original identity token stays protected in your SDK, and attackers intercepting network traffic get tokens that expire before they can use them.

Level 3: Request-Scoped Authorization (Next week)

Customer's SDK exchanges their identity token for short-lived authorization tokens:

from hessra import HessraClient

client = HessraClient(identity_token=api_key)

# SDK handles this automatically
auth_token = client.request_token("customers/123", "read")
response = requests.get(
    "https://api.yourservice.com/customers/123",
    headers={"Authorization": f"Bearer {auth_token}"}
)

Each request gets a token scoped to exactly what it needs. You can remove any baked-in authorization logic from your app and rely on Hessra authorization tokens. Get easy audit logs and identity revocation.

Level 3: Delegated Access (Next Month)

Your customer delegates access to their team members or services:

// Customer creates sub-identities for their team
let team_token = client.attenuate_identity_token(
    &api_key,
    "customer:acme-corp:team:engineering",
    Some(Utc::now() + Duration::days(7))
)?;

let intern_token = client.attenuate_identity_token(
    &team_token,
    "customer:acme-corp:team:engineering:intern-2024",
    Some(Utc::now() + Duration::hours(8))
)?;

Now you have:

  • Granular access control
  • Automatic expiration at each level
  • Clear audit trails showing delegation chains
  • Instant revocation by pruning the delegation tree

> Real-World Use Cases

SaaS API Access

Replace your current API key system entirely. Issue identity tokens to customers that:

  • Expire after 30/60/90 days (or whatever schedule is best for you)
  • Include the customer ID in the token itself
  • Can be verified by any microservice with just your public key
  • Leave clear audit trails of which customer made which request

Partner Integrations

Issue identity tokens to partners that they can delegate to their systems:

// Issue to partner company
let partner_token = create_identity_token(
    "partner:bigcorp",
    keypair,
    TokenTimeConfig::default()
)?;

// Partner delegates to their services
let service_token = partner.attenuate_identity_token(
    &partner_token,
    "partner:bigcorp:service:analytics",
    None
)?;

You see the full delegation chain in your logs: partner:bigcorp:service:analytics made request to /api/v1/data

Temporary Access Tokens

For operations that need time-limited access, just have an identity token minted with that period of time set. Something like 5 minutes or a password reset API or 24 hours to download a file.

No database entries, no cleanup jobs. They just stop working after expiration.

Multi-Tenant Systems

For SaaS platforms where customers need to grant access to consultants or tools:

  1. Customer gets their identity token (their "API key")
  2. Customer delegates to a consultant with restrictions
  3. Consultant's token only works for that customer's data
  4. When the consultant engagement ends, the token expires naturally
# Customer delegates to consultant
consultant_token = hessra_cli.delegate(
    from_token=customer_api_key,
    identity="customer:acme:consultant:security-audit-2024",
    ttl=604800  # 1 week engagement
)

# Consultant can only access this customer's data
# Token expires automatically after the engagement

> Trade-offs to Consider

Let's be honest about what you're getting and what you're giving up:

The Good

  • Automatic expiration without any backend infrastructure
  • Cryptographic security that can't be forged
  • Identity information embedded for audit trails
  • Offline verification with just a public key

The Trade-offs

Revocation is limited: Without a centralized revocation list, you can't surgically revoke individual tokens. Your options are:

  • Let tokens expire naturally (primary approach)
  • Rotate your public key (nuclear option - invalidates ALL tokens)
  • Check identities against a local blocklist during verification
  • Upgrade to Hessra's authorization service for stateful revocation (future)

Customer rotation burden: Your customers now need to handle token renewal. We recommend:

  • Long initial durations (30-90 days)
  • Clear documentation with renewal code examples
  • Email notifications before expiration (on our roadmap)
  • Automatic renewal in your SDKs

Performance overhead: Cryptographic verification is fast but not free:

  • ~0.1-0.5ms per verification on modern hardware
  • Comparable to a database query, but happens on every request
  • Use caching for hot paths if needed

Clock skew sensitivity: Time-based tokens can fail if clocks drift:

  • 5-second JIT tokens help mitigate this
  • Libraries handle reasonable skew (~30 seconds)
  • NTP sync recommended for production

Delegation constraints: Tokens can only narrow scope, never expand:

  • customer:acme can delegate to customer:acme:team but not customer:bigcorp
  • This is a security feature, not a bug

For detailed analysis of these trade-offs, see our documentation.

> Implementation Tips

Public Key Distribution

Your public key needs to be available to services that verify tokens. Options:

  1. Build-time: Include in your service images
  2. Config management: Distribute via Kubernetes ConfigMaps, Consul, etc.
  3. Well-known endpoint: Serve from /.well-known/hessra-public-key
  4. Fetch from Hessra: if using our service, just GET yourco.hessra.net/public_key

Token Rotation

Unlike traditional API keys, rotation is painless:

// Old token approaching expiration?
if info.expiry < (Utc::now().timestamp() + 7 * 86400) {
    // Issue a new one
    let new_token = client.request_identity_token(Some(&info.identity)).await?;
    // Customer uses new token going forward
}

Revocation Without State

Need to revoke a token? Update your public key. All existing tokens become invalid immediately. For more granular revocation, use delegation trees and prune specific branches. Or use the Hessra authorization service and get these features and more as they become available.

Migration Strategy

Moving from existing API keys? Run both systems in parallel:

fn verify_api_credential(auth_header: &str) -> Result<(), Error> {
    if auth_header.starts_with("Bearer hessra_") {
        // New identity token
        verify_bearer_token(extract_token(auth_header), public_key)
    } else if auth_header.starts_with("Bearer sk_") {
        // Legacy API key
        verify_legacy_api_key(extract_token(auth_header))
    } else {
        Err(Error::InvalidCredential)
    }
}

> Security Benefits

Switching to identity tokens for API keys gives you:

  1. No backend state: Tokens are self-contained and cryptographically verified
  2. Automatic expiration: Time limits enforced without any infrastructure
  3. Built-in identity: Know who's making requests without database lookups
  4. Delegation capabilities: Customers can self-service sub-keys for their teams
  5. Offline verification: Any service can verify with just the public key
  6. Clear upgrade path: Seamlessly grow into full authorization as needed using Hessra or your authorization system of choice

> Getting Started

Ready to replace your API keys with identity tokens?

  1. Install the SDK: cargo add hessra-sdk or pip install hessra-py
  2. Generate a keypair: For signing your identity tokens (or use Hessra service)
  3. Update your verification: Add bearer token verification to your services
  4. Issue tokens: Start creating identity tokens instead of random strings

Check out the complete examples in our SDK repository.

Need another language? We currently support Rust and Python, but we're eager to add more. If you need Go, Node.js, Java, or another language, reach out - we can likely add support quickly.

> The Future is Contextual

API keys were designed for a simpler time. Today's distributed systems need credentials that understand context: who's using them, when they expire, and what they're allowed to do.

Hessra identity tokens give you all of this while remaining as simple to use as traditional API keys. Start with them as bearer tokens today, and grow into full authorization tomorrow. No migration needed, just evolution.


Ready to upgrade your API keys? Check out our documentation or reach out for help with implementation.