Hessra Logo
< BACK_TO_DOCS
#authorization#getting-started#documentation#service-chains#multi-party#biscuit

Getting Started with Hessra Authorization

Learn how to use Hessra's authorization system with short-lived Biscuit tokens, service chains for microservices, and multi-party authorization for cross-organization security.

by The Hessra Team

Getting Started with Hessra Authorization

Hessra Authorization provides cryptographically verifiable, short-lived tokens for fine-grained access control. Unlike traditional authorization systems, Hessra tokens are designed to be verified offline, carry minimal authority, and support advanced patterns like service chains and multi-party authorization.

> Core Concepts

Authorization Tokens

Authorization tokens are the heart of Hessra's security model:

  • Short-lived: 5 minutes or less (configurable down to seconds)
  • Single-action: Scoped to one resource and operation
  • Meant to be used once: Request → Use → Discard pattern
  • Offline-verifiable: No database or network calls needed

Authentication Methods

Request authorization tokens using:

  • mTLS certificates: Traditional X.509 client certificates
  • Identity tokens: Hessra identity tokens (see identity documentation)

Policy Configuration

Authorization decisions are made based on GitOps-style configuration:

  • TOML files in a git repository
  • Versioned, auditable policy changes
  • Automatic sync with authorization service

> Basic Authorization Flow

1. Configure Your Policy

In your policy repository's clients.toml:

# Define which certificates can connect
[cacerts]
cert_chain = '''-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIUKb5f7cc...
-----END CERTIFICATE-----'''

# Define client permissions
[[clients]]
identifier = { type = "san_uri", value = "uri:urn:service:api-gateway" }
resources = [
  { name = "users", operations = ["read", "write", "delete"] },
  { name = "orders", operations = ["read", "create"] }
]

[[clients]]
identifier = { type = "san_dns", value = "worker-*.internal.com" }
resources = [
  { name = "queue", operations = ["read", "process"] }
]

2. Request Authorization Tokens

Using the Rust SDK:

use hessra_sdk::Hessra;

// Initialize with mTLS
let mut client = Hessra::builder()
    .base_url("auth.your-domain.com")
    .mtls_cert(MTLS_CERT)
    .mtls_key(MTLS_KEY)
    .server_ca(SERVER_CA)
    .build()?;

// Setup fetches the public key for offline verification
client.setup().await?;

// Request a token for specific action
let token = client
    .request_token("users", "read")
    .await?;

println!("Token received: {}...", &token[..50]);

Using the CLI:

hessra auth request \
  --server auth.your-domain.com \
  --cert client.crt \
  --key client.key \
  --ca ca.crt \
  --resource users \
  --operation read

3. Pass Tokens with Requests

Include the token when making requests to your services:

// HTTP request with authorization token
let response = reqwest::Client::new()
    .get("https://api.your-domain.com/users/123")
    .header("Authorization", format!("Bearer {}", token))
    .header("x-subject", "uri:urn:service:api-gateway")
    .header("x-resource", "users")
    .header("x-operation", "read")
    .send()
    .await?;

4. Verify Tokens in Services

Services verify tokens locally without network calls:

// In your service endpoint
fn verify_request(token: &str, subject: &str, resource: &str, operation: &str) -> Result<()> {
    // Verification is synchronous and offline
    client.verify_token_local(
        token,
        subject,
        resource,
        operation
    )?;

    // Token is valid - proceed with request
    Ok(())
}

> Advanced Feature: Service Chains

Service chains ensure requests pass through the correct sequence of services, preventing lateral movement and providing cryptographic proof of traversal.

Understanding Service Chains

In distributed systems, requests often traverse multiple services:

Client → API Gateway → Auth Middleware → Business Logic → Database

Service chains let each service:

  1. Verify the token is valid
  2. Confirm all previous services in the chain authorized it
  3. Add its own attestation for downstream services

Configuring Service Chains

In your resources.toml:

[resources.payment_processor]
type = "service_chain"
nodes = [
  {
    component = "api_gateway",
    public_key = "ed25519/e57618058b1d2e0381a9813c1405830d5ed7d603717384ef555d9cc0cfa65d83"
  },
  {
    component = "fraud_detection",
    public_key = "ed25519/78ef4255c4c9ab5c7186d6db4760758b06616c042a2538323ae3b058094034b6"
  },
  {
    component = "payment_processor",
    public_key = "ed25519/1aebc5a6eefc569051926a6aaf55568e53edb475f2f2eb904522609391d88113"
  }
]

Implementing Service Chains

Each service needs its own keypair and adds attestations:

// API Gateway (first service)
let api_gateway = Hessra::builder()
    .base_url("auth.your-domain.com")
    .mtls_cert(GATEWAY_CERT)
    .mtls_key(GATEWAY_KEY)
    .server_ca(CA_CERT)
    .personal_keypair(GATEWAY_KEYPAIR)  // Ed25519 or P-256 keypair
    .build()?;

// Request initial token
let token = api_gateway
    .request_token("payment_processor", "charge")
    .await?;

// Add attestation before forwarding
let attested_token = api_gateway
    .attest_service_chain_token(&token, "payment_processor")?;

// Forward to fraud detection service
forward_to_fraud_detection(attested_token);
// Fraud Detection Service (middle service)
let fraud_service = Hessra::builder()
    .base_url("auth.your-domain.com")
    .mtls_cert(FRAUD_CERT)
    .mtls_key(FRAUD_KEY)
    .server_ca(CA_CERT)
    .personal_keypair(FRAUD_KEYPAIR)
    .build()?;

// Verify the token came through API gateway
let partial_chain = ServiceChain::new()
    .with_node(ServiceNode {
        component: "api_gateway".to_string(),
        public_key: "ed25519/e57618058b1d2e0381a9813c1405830d5ed7d603717384ef555d9cc0cfa65d83".to_string()
    });

fraud_service.verify_service_chain_token_local(
    &token,
    "uri:urn:user:123",
    "payment_processor",
    "charge",
    &partial_chain,
    Some("fraud_detection")  // We are fraud_detection
)?;

// Add our attestation
let double_attested = fraud_service
    .attest_service_chain_token(&token, "payment_processor")?;

// Forward to payment processor
forward_to_payment_processor(double_attested);
// Payment Processor (final service)
let processor = Hessra::builder()
    .base_url("auth.your-domain.com")
    .mtls_cert(PROCESSOR_CERT)
    .mtls_key(PROCESSOR_KEY)
    .server_ca(CA_CERT)
    .personal_keypair(PROCESSOR_KEYPAIR)
    .build()?;

// Verify complete chain
let full_chain = ServiceChain::new()
    .with_node(ServiceNode {
        component: "api_gateway".to_string(),
        public_key: "ed25519/e57618058b1d2e0381a9813c1405830d5ed7d603717384ef555d9cc0cfa65d83".to_string()
    })
    .with_node(ServiceNode {
        component: "fraud_detection".to_string(),
        public_key: "ed25519/78ef4255c4c9ab5c7186d6db4760758b06616c042a2538323ae3b058094034b6".to_string()
    });

processor.verify_service_chain_token_local(
    &token,
    "uri:urn:user:123",
    "payment_processor",
    "charge",
    &full_chain,
    None  // We're the final service
)?;

// Token verified through complete chain - process payment
process_payment();

Service Chain Benefits

  1. Proof of Traversal: Cryptographic evidence of the exact path taken
  2. Lateral Movement Prevention: Compromised services can't bypass the chain
  3. Distributed Trust: No single service can forge the complete chain
  4. Audit Trail: Complete record of which services handled each request

> Advanced Feature: Multi-Party Authorization

Multi-party authorization requires multiple authorization services to approve a token, enabling cross-organization security policies.

Use Cases

Multi-party authorization is ideal for:

  • Cross-Organization Access: Two companies jointly authorizing data access
  • Business Unit Separation: Different departments must both approve
  • Vendor Software: Customer controls access to vendor-deployed software
  • Compliance Requirements: Multiple approval authorities required by regulation

How It Works

  1. Request token from primary authorization service
  2. Token includes pending_signoffs for other required services
  3. Each additional service signs the token based on their policy
  4. Final token is valid only with all signatures

Configuration

In your primary service's resources.toml:

[resources.sensitive_data]
type = "multi_party"
required_signers = [
  {
    name = "legal_dept",
    url = "https://legal.auth.company.com",
    public_key = "ed25519/a4b5c6d7e8f9..."
  },
  {
    name = "partner_company",
    url = "https://auth.partner.com",
    public_key = "ed25519/1a2b3c4d5e6f..."
  }
]

Implementation

Using the SDK's automatic collection:

// Request initial token
let token_response = client
    .request_token_full("sensitive_data", "export")
    .await?;

if let Some(pending) = &token_response.pending_signoffs {
    println!("Token requires {} additional signatures", pending.len());

    // Automatically collect all required signatures
    let signed_token = client
        .collect_multi_party_signoffs(
            token_response,
            "sensitive_data",
            "export"
        )
        .await?;

    println!("All signatures collected!");
    use_token(signed_token);
}

Manual signature collection:

// Get initial token
let mut current_token = token_response.token.unwrap();

// For each required signer
for signoff in token_response.pending_signoffs.unwrap() {
    // Create client for that authorization service
    let signer_client = Hessra::builder()
        .base_url(&signoff.url)
        .mtls_cert(&our_cert)  // Use your credentials
        .mtls_key(&our_key)
        .server_ca(&their_ca)
        .build()?;

    // Request their signature
    current_token = signer_client
        .sign_token(&current_token, "sensitive_data", "export")
        .await?;

    println!("Received signature from {}", signoff.name);
}

// Token now has all required signatures

Multi-Party Policy Coordination

Each authorization service evaluates based on its own policy:

Company A's policy:

[[clients]]
identifier = { type = "san_uri", value = "uri:urn:partner:service" }
resources = [
  { name = "sensitive_data", operations = ["export"] }
]
# Additional constraints: business hours only

Company B's policy:

[[clients]]
identifier = { type = "san_uri", value = "uri:urn:partner:service" }
resources = [
  { name = "sensitive_data", operations = ["export"] }
]
# Additional constraints: requires manager approval flag

Both policies must approve for the token to be valid.

> Configuration with GitOps

Hessra uses a GitOps approach for managing authorization policies. Your configuration lives in TOML files in a git repository, providing versioned, auditable control over authorization decisions.

Repository Structure

Your GitOps repository contains two main configuration files:

your-policy-repo/
├── clients.toml     # Client authentication and permissions
└── resources.toml   # Resource definitions and mechanisms

Configuring Client Authorization

The clients.toml file defines who can authenticate and what resources they can access:

# Version for compatibility tracking
version = "0.2.0"

# CA certificates that authenticate clients
[cacerts]
cert_chain = '''-----BEGIN CERTIFICATE-----
...your CA certificates...
-----END CERTIFICATE-----'''

# Service with broad permissions
[[clients]]
identifier = { type = "san_uri", value = "uri:urn:services:api-gateway" }
resources = [
  { name = "users", operations = ["read", "write", "delete"] },
  { name = "orders", operations = ["read", "write"] },
  { name = "payments", operations = ["read", "write"] }
]

# Microservice with specific permissions
[[clients]]
identifier = { type = "san_dns", value = "payment-processor.internal" }
resources = [
  { name = "payments", operations = ["read", "write", "process"] },
  { name = "audit_log", operations = ["write"] }
]

# Wildcard for worker fleet
[[clients]]
identifier = { type = "san_dns", value = "worker-*.internal.com" }
resources = [
  { name = "queue", operations = ["read", "process"] },
  { name = "metrics", operations = ["write"] }
]

# Client with identity token support
[[clients]]
identifier = { type = "serial", value = "1234567890" }
resources = [
  { name = "api", operations = ["read"] }
]
identity_token_enabled = true
identity_token_duration = 3600

Identifier Types

Hessra supports multiple ways to identify clients from certificates:

  • san_uri: Subject Alternative Name URI (e.g., uri:urn:services:api)
  • san_dns: DNS name with wildcard support (e.g., *.internal.com)
  • san_email: Email address
  • san_ip: IP address
  • serial: Certificate serial number

Configuring Resources and Mechanisms

The resources.toml file defines how resources are protected:

version = "0.2.0"

# Simple resource (default authorization)
[resources.users]
# No special configuration needed for basic resources

# Service chain resource
[resources.payment_processing]
mechanisms = ["service_chain"]

[resources.payment_processing.service_chain]
nodes = [
  {
    component = "api_gateway",
    public_key = "ed25519/e57618058b1d2e0381a9813c1405830d5ed7d603717384ef555d9cc0cfa65d83"
  },
  {
    component = "fraud_detection",
    public_key = "ed25519/78ef4255c4c9ab5c7186d6db4760758b06616c042a2538323ae3b058094034b6"
  },
  {
    component = "payment_processor",
    public_key = "ed25519/1aebc5a6eefc569051926a6aaf55568e53edb475f2f2eb904522609391d88113"
  }
]

# Multi-party authorization resource
[resources.cross_org_data]
mechanisms = ["multi_party"]

[resources.cross_org_data.multi_party]
required_signers = [
  {
    name = "partner_org",
    url = "https://auth.partner.com",
    public_key = "ed25519/a4b5c6d7e8f9a0b1c2d3e4f5667788990abbccddeeff0011223344556677889900"
  },
  {
    name = "compliance_dept",
    url = "https://compliance.internal.com",
    public_key = "ed25519/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
  }
]

# Mixed mechanism resource (future)
[resources.sensitive_operation]
mechanisms = ["service_chain", "multi_party"]
# Configuration for both mechanisms...

GitOps Workflow

The authorization service automatically syncs with your git repository:

  1. Push Changes: Update configuration files in your repository
  2. Automatic Pull: Authorization service detects and pulls changes
  3. Validation: Configurations are validated before applying
  4. Live Update: New policies take effect without service restart
  5. Rollback: Revert git commits to rollback policy changes

Policy Validation

The authorization service validates configurations before applying:

# Local validation (if using Hessra CLI tools)
hessra policy validate --path ./policy-repo

# Validation checks:
# - TOML syntax correctness
# - Certificate chain validity
# - Public key format validation
# - Resource reference consistency
# - No conflicting permissions

Best Practices for Configuration

Resource Naming

Use consistent, hierarchical naming:

# Good
resources = [
  { name = "api.users", operations = ["read", "write"] },
  { name = "api.orders", operations = ["read"] },
  { name = "db.users", operations = ["read"] }
]

# Avoid
resources = [
  { name = "users", operations = ["read"] },
  { name = "UserData", operations = ["write"] },
  { name = "user-info", operations = ["read"] }
]

Operation Standards

Define consistent operation names across resources:

  • CRUD: create, read, update, delete
  • Special: execute, process, approve, audit
  • Batch: bulk_read, bulk_write, bulk_delete

Permission Minimization

Grant minimum necessary permissions:

# Too broad
[[clients]]
identifier = { type = "san_uri", value = "uri:urn:service:worker" }
resources = [
  { name = "*", operations = ["*"] }  # Avoid wildcards
]

# Better
[[clients]]
identifier = { type = "san_uri", value = "uri:urn:service:worker" }
resources = [
  { name = "queue", operations = ["read", "process"] },
  { name = "metrics", operations = ["write"] }
]

Service Chain Configuration

Always list nodes in order of expected traversal:

# Correct order: edge → middle → backend
nodes = [
  { component = "cdn", public_key = "..." },
  { component = "api_gateway", public_key = "..." },
  { component = "backend", public_key = "..." }
]

Environment-Specific Configuration

Use branches or separate repositories for different environments:

# Repository structure
main              # Production policies
├── staging       # Staging environment
└── development   # Development environment

# Or separate repositories
policy-prod/      # Production only
policy-staging/   # Staging only
policy-dev/       # Development only

Audit and Compliance

GitOps provides automatic audit trails:

  • Who: Git commit author
  • What: Diff of configuration changes
  • When: Commit timestamp
  • Why: Commit message with ticket reference
  • Approval: PR reviews and merge approvals

Example commit message:

feat(auth): Grant payment service access to audit logs

- Added write permission for audit_log resource
- Required for compliance with PCI-DSS logging

Ticket: SEC-1234
Reviewed-by: security-team

> Integration Patterns

Database Integration with PostgreSQL Extension

Use the Hessra PGRX extension for Row Level Security:

-- Install the extension
CREATE EXTENSION IF NOT EXISTS hessra_authz;

-- Create RLS policy using Hessra verification
CREATE POLICY user_data_access ON user_data
FOR SELECT
USING (
  hessra_authz.verify_token(
    current_setting('hessra.token'),
    current_setting('hessra.subject'),
    'user_data',
    'read'
  )
);

-- In your application
SET LOCAL hessra.token = 'your_token_here';
SET LOCAL hessra.subject = 'uri:urn:user:123';
SELECT * FROM user_data;  -- RLS automatically enforced

Microservice Mesh Pattern

Implement zero-trust between all services:

// Middleware for automatic token verification
async fn auth_middleware(req: Request, next: Next) -> Response {
    let token = match req.headers().get("authorization") {
        Some(h) => h.to_str().unwrap().replace("Bearer ", ""),
        None => return Response::unauthorized()
    };

    let subject = req.headers().get("x-subject").unwrap();
    let resource = req.headers().get("x-resource").unwrap();
    let operation = req.headers().get("x-operation").unwrap();

    match verify_token(&token, subject, resource, operation) {
        Ok(_) => next.run(req).await,
        Err(_) => Response::forbidden()
    }
}

Event-Driven Systems

Include tokens in event payloads:

{
  "event_id": "evt_123",
  "type": "order.created",
  "data": {
    /* ... */
  },
  "authorization": {
    "token": "biscuit_token_here",
    "subject": "uri:urn:service:order-processor",
    "resource": "orders",
    "operation": "create"
  }
}

> Performance Considerations

Token Verification Performance

Local verification benchmarks:

  • Simple token: ~50μs
  • Service chain (3 nodes): ~150μs
  • Multi-party (3 signers): ~200μs

Caching Strategies

Cache tokens for repeated operations:

// Token cache for batch operations
struct TokenCache {
    tokens: HashMap<(String, String), (String, Instant)>,
}

impl TokenCache {
    async fn get_token(&mut self, resource: &str, operation: &str) -> Result<String> {
        let key = (resource.to_string(), operation.to_string());

        if let Some((token, issued)) = self.tokens.get(&key) {
            if issued.elapsed() < Duration::from_secs(240) {  // 4 minutes
                return Ok(token.clone());
            }
        }

        let token = request_new_token(resource, operation).await?;
        self.tokens.insert(key, (token.clone(), Instant::now()));
        Ok(token)
    }
}

Batch Authorization

Request multiple tokens in parallel:

use futures::future::join_all;

let requests = vec![
    client.request_token("users", "read"),
    client.request_token("orders", "create"),
    client.request_token("inventory", "update"),
];

let tokens = join_all(requests).await;

> Troubleshooting

Common Issues

Token Expired

Error: Token has expired
  • Tokens are short-lived by design
  • Request new token just before use
  • Consider your network latency when setting TTL

Service Chain Verification Failed

Error: Missing attestation from: fraud_detection
  • Ensure services attest in correct order
  • Verify public keys match configuration
  • Check service keypairs are correctly configured

Multi-Party Signature Missing

Error: Required signature from legal_dept not found
  • Ensure all required services are reachable
  • Verify mTLS credentials work with each service
  • Check each service's policy allows the operation

Debug Commands

# Decode and inspect a token
hessra token inspect --token "biscuit_..."

# Test service chain configuration
hessra chain verify \
  --token "biscuit_..." \
  --chain-config chain.toml

# Validate policy configuration
hessra policy validate --path ./policy-repo

> Best Practices

Token Lifetime

Keep tokens short-lived:

  • Default: 5 minutes
  • High-security: 30-60 seconds
  • Batch operations: Match batch duration
  • Never exceed 10 minutes

Request Patterns

  1. Just-in-time: Request tokens immediately before use
  2. Least privilege: Request minimum necessary scope
  3. Single use: Don't reuse tokens across operations
  4. Fail secure: Deny access on any verification failure

Policy Management

  1. Version control: Keep policies in git
  2. Review process: Require approval for policy changes
  3. Test environments: Validate policies before production
  4. Audit regularly: Review and prune unused permissions

> Migration Guide

From JWT/OAuth

// Before (JWT)
let claims = decode_jwt(&token)?;
if claims.scopes.contains("users:read") {
    // Allow access
}

// After (Hessra)
client.verify_token_local(&token, subject, "users", "read")?;
// Cryptographically verified, offline, with proof

From API Keys

// Before (API Key)
if is_valid_api_key(&request.api_key) {
    // Check permissions in database
    let perms = db.get_permissions(&request.api_key).await?;
    if perms.can_read_users {
        // Allow access
    }
}

// After (Hessra)
client.verify_token_local(&token, subject, "users", "read")?;
// No database lookup, cryptographically secure

> Next Steps

  1. Review identity tokens for authentication
  2. Explore our SDK examples
  3. Check the API documentation
  4. Join our community Discord

Ready to implement secure, verifiable authorization? Contact us for enterprise support or visit our GitHub for open source SDKs.