# How OpenHuman's JSON-RPC Server Implements Bearer-Token Authentication and CORS

> Learn how OpenHuman's JSON-RPC server secures requests using Bearer-token authentication and configures CORS. Explore the Axum middleware stack for robust API security.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: how-to-guide
- Published: 2026-08-24

---

**The OpenHuman JSON-RPC server uses a layered Axum middleware stack where a custom authentication layer validates Bearer tokens against the `OPENHUMAN_CORE_TOKEN` environment variable, while a `tower_http` CORS layer handles cross-origin requests before protected requests reach the `/rpc` endpoint.**

OpenHuman is an open-source AI platform that exposes its core functionality via a JSON-RPC API running on an Axum HTTP server. Understanding how this server enforces security through bearer-token authentication while enabling browser-based clients via CORS configuration is essential for developers integrating with the platform.

## Bearer-Token Authentication Architecture

The authentication system relies on a shared secret token that must be presented with every JSON-RPC request.

### Token Generation and Storage

The expected bearer token is sourced from the environment variable `OPENHUMAN_CORE_TOKEN`. If this variable is not set at startup, the system generates a cryptographically secure token automatically. This logic resides in [`src/openhuman/security/pairing.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/pairing.rs), ensuring each OpenHuman instance possesses a unique secret for the session.

### The Authentication Middleware Layer

Incoming requests pass through a custom Axum layer defined in [`src/core/jsonrpc/auth_middleware.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/jsonrpc/auth_middleware.rs). This middleware:

- Extracts the `Authorization` header from the request
- Validates that it contains a `Bearer <token>` string
- Compares the provided token against the stored `OPENHUMAN_CORE_TOKEN` value
- Returns `401 Unauthorized` immediately if the header is missing, malformed, or incorrect

The middleware is applied globally to the router, meaning every request to `/rpc` must present the valid bearer token before reaching the JSON-RPC handler.

## CORS Configuration for Cross-Origin Requests

To support browser-based clients and desktop UI interactions, the server implements Cross-Origin Resource Sharing (CORS) through the `tower_http` crate.

### CorsLayer Implementation

The CORS configuration is constructed in [`src/core/jsonrpc/cors.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/jsonrpc/cors.rs) using `tower_http::cors::CorsLayer`. This layer is designed to permit requests from authorized origins while maintaining security boundaries.

### Allowed Origins, Methods, and Headers

The layer configuration varies by environment:

- **Development**: Allows all origins (`*`) to facilitate local testing
- **Production**: Restricts to specific authorized origins

Regardless of environment, the layer explicitly permits:

- HTTP methods: `POST` and `OPTIONS` (the only methods required for JSON-RPC)
- Headers: `Content-Type` and `Authorization` (via `allow_headers([axum::http::header::AUTHORIZATION])`)

### Pre-flight Request Handling

When browsers send `OPTIONS` pre-flight requests to verify CORS permissions, the `CorsLayer` intercepts these before they reach the authentication middleware. The layer automatically responds with appropriate CORS headers, preventing unnecessary token exposure to pre-flight checks and avoiding `401` errors on legitimate cross-origin negotiations.

## Middleware Pipeline and Request Flow

The router composition follows a specific layering order to ensure correct request processing. As defined in [`src/core/jsonrpc/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/jsonrpc/mod.rs), the middleware stack looks like:

```rust
let router = Router::new()
    .route("/rpc", post(jsonrpc_handler))
    .layer(cors_layer())                // src/core/jsonrpc/cors.rs
    .layer(auth_layer(token.clone()));  // src/core/jsonrpc/auth_middleware.rs

```

This ordering is critical because Axum executes layers from top to bottom during request processing:

1. **CORS Layer** processes the request first. If it is an `OPTIONS` pre-flight, the response returns immediately with CORS headers.
2. **Authentication Layer** executes for actual `POST` requests, verifying the bearer token before the request reaches the handler in [`src/core/jsonrpc/handler.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/jsonrpc/handler.rs).
3. **JSON-RPC Handler** receives the request only if both preceding checks pass.

## Implementation Examples

### Server Setup

The following Rust code demonstrates how OpenHuman initializes the JSON-RPC server with both security layers:

```rust
use axum::{Router, routing::post};
use openhuman::core::jsonrpc::{auth_layer, cors_layer, handler};

let token = std::env::var("OPENHUMAN_CORE_TOKEN")
    .expect("core bearer token missing");

// Build the router with both layers.
let app = Router::new()
    .route("/rpc", post(handler))
    .layer(cors_layer())
    .layer(auth_layer(token));

// Run the server – typically bound to 127.0.0.1 with a random port.
axum::Server::bind(&([127, 0, 0, 1], 0).into())
    .serve(app.into_make_service())
    .await
    .unwrap();

```

### Rust Client Implementation

Clients must include the bearer token in the `Authorization` header:

```rust
use reqwest::Client;

let client = Client::new();
let bearer = std::env::var("OPENHUMAN_CORE_TOKEN").unwrap();

let resp = client
    .post("http://127.0.0.1:12345/rpc")
    .bearer_auth(bearer)               // adds `Authorization: Bearer <token>`
    .json(&json!({ "jsonrpc":"2.0", "method":"core.ping", "id":1 }))
    .send()
    .await?;

```

### Browser Client with Fetch

Web applications can interact with the API provided they handle CORS correctly:

```javascript
fetch("http://127.0.0.1:12345/rpc", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + token   // token obtained from the app environment
  },
  body: JSON.stringify({jsonrpc:"2.0", method:"core.ping", id:1})
})
.then(r => r.json())
.then(console.log);

```

## Summary

- OpenHuman stores the JSON-RPC bearer token in `OPENHUMAN_CORE_TOKEN`, auto-generating it in [`src/openhuman/security/pairing.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/pairing.rs) if not provided.
- The [`auth_middleware.rs`](https://github.com/tinyhumansai/openhuman/blob/main/auth_middleware.rs) layer enforces token validation on every `/rpc` request, returning `401 Unauthorized` for invalid or missing credentials.
- CORS handling in [`src/core/jsonrpc/cors.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/jsonrpc/cors.rs) uses `tower_http::cors::CorsLayer` to permit cross-origin `POST` and `OPTIONS` requests while exposing the `Authorization` header.
- Middleware ordering places CORS before authentication, ensuring pre-flight requests receive proper headers without triggering auth failures.
- The handler in [`src/core/jsonrpc/handler.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/jsonrpc/handler.rs) only processes requests that pass both the CORS and bearer-token checks.

## Frequently Asked Questions

### How does OpenHuman generate the JSON-RPC bearer token if the environment variable is missing?

If `OPENHUMAN_CORE_TOKEN` is not set, OpenHuman automatically generates a cryptographically secure bearer token during startup. This generation logic is implemented in [`src/openhuman/security/pairing.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/pairing.rs), ensuring the server always has a valid secret for authenticating requests without manual configuration.

### Why does the CORS layer return 401 errors on pre-flight OPTIONS requests?

The CORS layer should not return 401 errors if configured correctly. In OpenHuman's architecture, the `CorsLayer` from `tower_http` is applied **before** the authentication middleware in the Axum router. This ordering ensures that `OPTIONS` pre-flight requests are handled and responded to by the CORS layer before the auth middleware checks for bearer tokens, preventing unauthorized errors on legitimate cross-origin negotiations.

### Can I customize the allowed origins for the JSON-RPC CORS policy?

Yes, the CORS configuration in [`src/core/jsonrpc/cors.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/jsonrpc/cors.rs) can be modified to restrict origins. While development builds typically use a permissive wildcard (`*`), production deployments should configure `CorsLayer` with a specific list of authorized domains to restrict cross-origin access to trusted clients only.

### What happens if I send a request to `/rpc` without the Authorization header?

The authentication middleware defined in [`src/core/jsonrpc/auth_middleware.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/jsonrpc/auth_middleware.rs) immediately rejects any request lacking a valid `Authorization: Bearer <token>` header with a `401 Unauthorized` response. The JSON-RPC handler in [`src/core/jsonrpc/handler.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/jsonrpc/handler.rs) is never invoked for unauthenticated requests.