Skip to content

Outpost OAuth JWT Validation

By default, the Arbitex outpost authenticates proxy requests using API keys. You can additionally enable OAuth JWT validation so that clients can authenticate with M2M Bearer tokens issued by the Arbitex platform (or any compatible OAuth 2.0 authorization server).

When JWT validation is enabled, the outpost accepts requests with an Authorization: Bearer <jwt> header and validates the token’s signature, expiry, and scope before proxying the request.


  • PyJWT library must be installed in the outpost Python environment. If PyJWT is not available, JWT validation is disabled regardless of configuration. Check with python -c "import jwt; print(jwt.__version__)".
  • RSA public key or JWKS URL must be configured via environment variables.

JWT validation is controlled by two environment variables. At least one must be set to enable validation.

Set OAUTH_JWT_PUBLIC_KEY to a PEM-encoded RSA public key:

Terminal window
export OAUTH_JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----"

Or pass it as a file path (set the variable to the file contents):

Terminal window
export OAUTH_JWT_PUBLIC_KEY=$(cat /etc/arbitex/oauth_public_key.pem)

This is the recommended approach for production deployments. The public key must correspond to the private key used to sign JWTs issued by the Arbitex platform or your authorization server.

Set OAUTH_JWKS_URL to the JWKS endpoint URL of your authorization server:

Terminal window
export OAUTH_JWKS_URL="https://api.arbitex.ai/.well-known/jwks.json"

Checking whether JWT validation is enabled

Section titled “Checking whether JWT validation is enabled”

The outpost exposes a health endpoint that includes JWT validation status:

GET /admin/status
{
"jwt_validation": {
"enabled": true,
"mode": "public_key_pem"
}
}

mode is one of "public_key_pem", "jwks_url", or absent when disabled.


When the outpost receives an Authorization: Bearer <token> header and JWT validation is enabled, it performs the following checks in order:

  1. Structural check — token must have exactly three dot-separated segments.
  2. Algorithm check — the JWT alg header must be RS256, RS384, or RS512. Tokens signed with any other algorithm (including HS256) are rejected with 401.
  3. Signature verification — the signature is verified against the configured RSA public key.
  4. Expiry check — the exp claim must not be in the past. A 60-second clock skew tolerance is applied to accommodate minor time differences between the issuer and outpost.
  5. Issued-at check — the iat claim is verified with the same 60-second leeway.
  6. Scope check — the scope claim must contain the required scope (default: api:write). The scope claim may be a space-separated string or a JSON array.

The required scope is api:write by default. Tokens must contain this scope in the scope claim.

The scope claim may be formatted as a space-separated string or a JSON array:

// Space-separated string (RFC 6749 §3.3)
{"scope": "api:write api:read"}
// JSON array
{"scope": ["api:write", "api:read"]}

Both formats are accepted. A token containing api:write in either format passes the scope check.


JWT validation is additive, not exclusive. The authentication chain works as follows:

  1. If the Authorization: Bearer header is present and JWT validation is enabled, the token is validated.
    • Valid JWT → request proceeds.
    • Invalid JWT → 401 or 403 returned immediately. API key fallback is not attempted.
  2. If JWT validation is not enabled, or if no Authorization header is present, the outpost falls through to API key authentication.

This means that disabling JWT validation (by removing the environment variables) transparently reverts to API-key-only authentication with no other configuration changes required.


ConditionHTTP statusDescription
Token expired401exp claim is in the past (after 60s tolerance)
Signature invalid401RSA signature verification failed
Malformed JWT401Token cannot be decoded or has wrong structure
Unsupported algorithm401alg header is not RS256, RS384, or RS512
Missing required scope403scope claim does not contain the required scope
JWKS URL configured but not implemented501JWKS fetch path is not yet available

Obtaining tokens for outpost authentication

Section titled “Obtaining tokens for outpost authentication”

Tokens used for outpost JWT authentication must be issued by a compatible authorization server using an RSA signing key. The Arbitex platform M2M token endpoint (POST /api/oauth/token) currently issues HS256 tokens, which are not accepted by the outpost JWT validator (RSA algorithms only).

To authenticate against the outpost using JWTs, you need an authorization server that issues RS256-signed tokens, or to configure the outpost with an API key for platform-issued M2M tokens.

If your deployment requires outpost JWT auth with platform-issued tokens, contact Arbitex support — the RS256 upgrade path for the platform token endpoint is documented in the source and planned for a future release.


  • Never set OAUTH_JWT_PUBLIC_KEY to a private key. The outpost only needs the public key for signature verification. Treat the private key as a secret and store it only in your authorization server.
  • Algorithm restriction is enforced on the outpost side. Even if a JWT header claims alg: none or alg: HS256, the outpost rejects the token. Algorithm confusion attacks are mitigated by the strict allowlist.
  • Clock skew tolerance is 60 seconds. If outpost system time drifts more than 60 seconds from the issuer, valid tokens may be rejected. Monitor NTP synchronization on outpost nodes.