Skip to main content
CoreSDK
Authentication

JWT Authentication

How CoreSDK verifies JWTs and extracts user context.

JWT Authentication

CoreSDK supports RS256, ES256, and PS256 JWT verification with automatic JWKS key rotation. Verification happens in the sidecar (Python/Go/TS) or in-process (Rust).

Configuration

Set via coresdk.toml or env vars:

[sdk]
sidecar_addr = "[::1]:50051"
tenant_id    = "acme-corp"
fail_mode    = "open"
# Point to your IdP JWKS endpoint
export CORESDK_JWKS_URL=https://your-idp.com/.well-known/jwks.json
export CORESDK_FAIL_MODE=closed   # deny on sidecar error in production
from coresdk import CoreSDKClient, SDKConfig

_sdk = CoreSDKClient(SDKConfig(
    sidecar_addr="[::1]:50051",
    tenant_id="acme-corp",
    fail_mode="open",
))
use coresdk_engine::{Engine, EngineConfig};

let engine = Engine::from_env()?;
// CORESDK_JWKS_URL=https://your-idp.com/.well-known/jwks.json

Or explicit config:

let engine = Engine::new(EngineConfig {
    jwks_url: Some("https://your-idp.com/.well-known/jwks.json".into()),
    tenant_id: "acme-corp".into(),
    fail_mode: coresdk_engine::FailMode::Open,
    ..Default::default()
})?;

Supported algorithms

AlgorithmKey typeNotes
RS256RSA 2048+Most common; Auth0/Okta default
ES256ECDSA P-256Faster; Clerk default
PS256RSA-PSSFIPS-compatible

How verification works

  1. Middleware extracts Authorization: Bearer <token>
  2. JWT header parsed → kid extracted
  3. JWKS fetched from CORESDK_JWKS_URL (cached, refreshed on unknown kid)
  4. Signature verified against matching JWK
  5. Claims validated: exp, iat, nbf (clock skew: 30s)
  6. Claims stored on request.state.coresdk_user (FastAPI), g.claims (Flask), request.coresdk_claims (Django)

JWKS keys are cached in memory with ArcSwap (lock-free reads on hot path). Key rotation is zero-downtime — unknown kid triggers a JWKS refresh.

Claims extracted

{
  "sub":       "alice",
  "tenant_id": "acme-corp",
  "roles":     ["viewer", "editor"],
  "email":     "alice@acme.com",
  "exp":       1234567890
}

Custom claims from your IdP are included as-is. The tenant_id claim is required for multi-tenancy.

Fail modes

fail_modeSidecar unreachableInvalid token
openAllow, claims = NoneDeny (401)
closedDeny (503)Deny (401)
CORESDK_FAIL_MODE=open    # development — allow on sidecar error
CORESDK_FAIL_MODE=closed  # production — deny on any auth failure

Validate a token manually

decision = _sdk.validate_token("eyJ...")
if decision.allowed:
    print(decision.claims)   # {"sub": "alice", "tenant_id": ...}
else:
    print(decision.reason)   # "token-expired", "fail-open", etc.

Without a real IdP (development)

Without CORESDK_JWKS_URL, the sidecar accepts any bearer token in fail_mode="open". You can pass any string as the token and get back allowed=True with claims=None.

curl -H "Authorization: Bearer alice-token" http://localhost:8000/me
# → 200 with {"sub": "anonymous"} (fail-open)

On this page