How OpenHuman's JSON-RPC Server Implements Bearer-Token Authentication and CORS
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, 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. This middleware:
- Extracts the
Authorizationheader from the request - Validates that it contains a
Bearer <token>string - Compares the provided token against the stored
OPENHUMAN_CORE_TOKENvalue - Returns
401 Unauthorizedimmediately 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 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:
POSTandOPTIONS(the only methods required for JSON-RPC) - Headers:
Content-TypeandAuthorization(viaallow_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, the middleware stack looks like:
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:
- CORS Layer processes the request first. If it is an
OPTIONSpre-flight, the response returns immediately with CORS headers. - Authentication Layer executes for actual
POSTrequests, verifying the bearer token before the request reaches the handler insrc/core/jsonrpc/handler.rs. - 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:
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:
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:
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 insrc/openhuman/security/pairing.rsif not provided. - The
auth_middleware.rslayer enforces token validation on every/rpcrequest, returning401 Unauthorizedfor invalid or missing credentials. - CORS handling in
src/core/jsonrpc/cors.rsusestower_http::cors::CorsLayerto permit cross-originPOSTandOPTIONSrequests while exposing theAuthorizationheader. - Middleware ordering places CORS before authentication, ensuring pre-flight requests receive proper headers without triggering auth failures.
- The handler in
src/core/jsonrpc/handler.rsonly 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, 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 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 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 is never invoked for unauthenticated requests.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →