# How JWT Authentication Works Between OP Stack Services in Base

> Discover how JWT authentication secures communication between OP Stack services in Base. Learn how a shared JWT secret enables secure interactions for your node and execution clients.

- Repository: [Base/node](https://github.com/base/node)
- Tags: deep-dive
- Published: 2026-03-03

---

**Base uses a shared 256-bit JWT secret injected via environment variables to authenticate communication between the OP Node and execution clients like Geth, Reth, and Nethermind.**

The `base/node` repository implements JWT authentication to secure the engine API communication between the L2 rollup node and underlying execution clients. This mechanism ensures that only authorized clients can interact with the rollup's state-transition engine, preventing unauthorized access to the critical consensus layer. The implementation relies on a pre-shared secret passed through environment variables rather than hardcoded credentials or TLS certificates.

## Shared Secret Generation and Configuration

### Environment Variable Setup

Authentication begins with a 256-bit secret generated beforehand and stored as a raw hex string. The repository defines this in environment files like `.env.sepolia` and `.env.mainnet` using the variable `OP_NODE_L2_ENGINE_AUTH_RAW`. The destination file path is specified by `OP_NODE_L2_ENGINE_AUTH`, which defaults to `/tmp/engine-auth-jwt` if not explicitly set.

```bash

# From .env.sepolia

OP_NODE_L2_ENGINE_AUTH_RAW=688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a
OP_NODE_L2_ENGINE_AUTH=/tmp/engine-auth-jwt

```

### Writing the Secret to Disk

Each service's entry-point script writes this raw value to the temporary file before launching the client binary. This ensures the secret remains outside the container image and is injected at runtime.

```bash
echo "$OP_NODE_L2_ENGINE_AUTH_RAW" > "$OP_NODE_L2_ENGINE_AUTH"

```

*Source: `op-node-entrypoint` writes the secret at lines 45-47, while execution clients perform similar operations in their respective entry-points.*

## Execution Client Configuration

Each supported execution client reads the JWT secret from the shared file path and uses it to sign requests to the OP Node's engine API.

### Geth Configuration

In `geth/geth-entrypoint`, the client launches with the `--authrpc.jwtsecret` flag pointing to the environment-defined path:

```bash
geth \
  --authrpc.jwtsecret="$OP_NODE_L2_ENGINE_AUTH" \
  # additional configuration flags

```

*Reference: Lines 31-65 in `geth/geth-entrypoint` handle the secret writing and Geth initialization.*

### Reth Configuration

The Reth client follows an identical pattern in `reth/reth-entrypoint`, using the same flag name as Geth:

```bash
reth \
  --authrpc.jwtsecret="$OP_NODE_L2_ENGINE_AUTH" \
  # additional configuration flags

```

*Reference: Lines 111-130 in `reth/reth-entrypoint` implement this configuration.*

### Nethermind Configuration

Nethermind uses a different flag name but the same file path. In `nethermind/nethermind-entrypoint`, the configuration uses:

```bash
nethermind \
  --JsonRpc.JwtSecretFile="$OP_NODE_L2_ENGINE_AUTH" \
  # additional configuration flags

```

*Reference: Lines 26-55 in `nethermind/nethermind-entrypoint` handle the JWT setup for this client.*

## OP Node Verification

The OP Node binary reads the same secret file to validate incoming requests. The `op-node-entrypoint` script writes the secret to disk before executing the binary:

```bash
echo "$OP_NODE_L2_ENGINE_AUTH_RAW" > "$OP_NODE_L2_ENGINE_AUTH"
exec ./op-node

```

When the OP Node receives engine API calls from execution clients, it verifies the JWT signature against this shared secret. Any request lacking a valid signature or using a mismatched secret is rejected, ensuring only the authenticated execution client can trigger state transitions.

## Authentication Flow

The JWT authentication between OP Stack services follows this sequence:

1. **Secret distribution** – All services read the identical 256-bit hex value from `OP_NODE_L2_ENGINE_AUTH_RAW`
2. **File initialization** – Each entry-point writes the secret to `/tmp/engine-auth-jwt` (or the configured path)
3. **Request signing** – Execution clients create JWTs signed with the secret when calling the engine API
4. **Signature validation** – The OP Node verifies tokens using the same secret, accepting only authenticated requests

This design provides **mutual authentication** without requiring TLS client certificates or network-level encryption between the components. The secret remains ephemeral, existing only in memory and temporary files during the container lifecycle.

## Summary

- **Shared secret architecture** – Base uses a single 256-bit JWT secret shared between the OP Node and all execution clients (Geth, Reth, Nethermind)
- **Environment-driven configuration** – The secret is injected via `OP_NODE_L2_ENGINE_AUTH_RAW` and written to a temporary file defined by `OP_NODE_L2_ENGINE_AUTH`
- **Client-specific flags** – Geth and Reth use `--authrpc.jwtsecret`, while Nethermind uses `--JsonRpc.JwtSecretFile` to reference the secret file
- **Runtime security** – Secrets are never baked into container images; they are mounted at runtime via environment variables and entry-point scripts
- **Engine API protection** – All engine API communication requires valid JWT signatures, preventing unauthorized state manipulation

## Frequently Asked Questions

### What is the purpose of JWT authentication in the OP Stack?

JWT authentication secures the engine API endpoints that allow execution clients to interact with the consensus layer. According to the `base/node` source code, this prevents unauthorized clients from submitting execution payloads or fork choice updates to the OP Node, protecting the integrity of the L2 chain's state transitions.

### How is the JWT secret shared between services?

The secret is distributed via the `OP_NODE_L2_ENGINE_AUTH_RAW` environment variable, which all services read during startup. Each entry-point script (`geth-entrypoint`, `reth-entrypoint`, `nethermind-entrypoint`, and `op-node-entrypoint`) writes this value to the same file path before launching the respective binary, ensuring all components share the identical authentication key.

### Where is the JWT secret stored in Base's configuration?

The secret is defined in environment files (`.env.sepolia`, `.env.mainnet`) and mounted as the `OP_NODE_L2_ENGINE_AUTH_RAW` variable. It is written to a temporary file at `/tmp/engine-auth-jwt` (or the path specified by `OP_NODE_L2_ENGINE_AUTH`) during container initialization. The repository contains no hardcoded secrets; operators must generate and provide their own 256-bit hex values.

### Do all execution clients use the same JWT configuration format?

While all clients read from the same file path, they use different CLI flags. Geth and Reth both use `--authrpc.jwtsecret`, but Nethermind requires `--JsonRpc.JwtSecretFile`. Despite these syntactic differences in `base/node`, all implementations achieve the same cryptographic result: signing requests with the HMAC-SHA256 algorithm using the shared 256-bit secret.