惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

云风的 BLOG
云风的 BLOG
M
MIT News - Artificial intelligence
Recent Announcements
Recent Announcements
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Stack Overflow Blog
Stack Overflow Blog
J
Java Code Geeks
Microsoft Azure Blog
Microsoft Azure Blog
罗磊的独立博客
博客园 - 【当耐特】
H
Help Net Security
腾讯CDC
大猫的无限游戏
大猫的无限游戏
GbyAI
GbyAI
Last Week in AI
Last Week in AI
Jina AI
Jina AI
博客园 - 聂微东
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
Apple Machine Learning Research
Apple Machine Learning Research
P
Proofpoint News Feed
Y
Y Combinator Blog
C
Check Point Blog
博客园 - 司徒正美
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Validate JWTs from Multiple Issuers in kgateway
Emmanuel Chu · 2026-05-17 · via DEV Community

Production APIs often need to accept tokens from more than one identity provider for example, a tenant's own Auth0 tenant and Google Workspace for internal tools. kgateway's JWTPolicy resource lets you declare multiple issuers in one policy and attach it to any HTTPRoute, so you don't need a separate gateway per IdP.

This guide walks through a working, reproducible configuration. By the end you will have a policy that validates tokens from two issuers, rejects mismatched audiences, and forwards selected claims as upstream headers.


What is a JWT?

A JSON Web Token (JWT) is a compact, self-contained credential that an identity provider (IdP) issues to a user or service after they authenticate. Instead of your API checking a username and password on every request, the client attaches a JWT and your API trusts it because it was cryptographically signed by someone it already trusts.

Think of it like a signed event wristband. The venue (IdP) checks your ID once at the gate and gives you a wristband. Staff inside the venue (your APIs) can verify the wristband is genuine without phoning the front gate again. The wristband also says which areas you can access and it expires at midnight.

Structure of a JWT

A JWT is three Base64URL-encoded JSON objects joined by dots:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9   ← Header
.
eyJzdWIiOiJ1c2VyXzEyMyIsImVtYWlsIjoiYWxpY2VAZXhhbXBsZS5jb20i...  ← Payload
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c  ← Signature

Enter fullscreen mode Exit fullscreen mode

Part What it contains
Header Algorithm (RS256) and token type. Tells the verifier how to check the signature.
Payload Claims about the user and the token who issued it, who it's for, when it expires.
Signature Cryptographic proof the token hasn't been tampered with. Verified against the IdP's public key.

You can paste any JWT into jwt.io to decode it instantly.

What's inside the payload?

The payload is a JSON object of claims statements about the token and its subject. Some are standard; some are custom fields added by your IdP.

{
  "iss": "https://my-tenant.auth0.com/",  // issuer  who created the token
  "sub": "user_123",                       // subject  the user's unique ID
  "aud": "my-api",                         // audience  which service this token is for
  "exp": 1717000000,                       // expiry  Unix timestamp
  "email": "alice@example.com",            // custom claim added by Auth0
  "roles": ["admin", "editor"]             // custom claim for RBAC
}

Enter fullscreen mode Exit fullscreen mode

How signature verification works (JWKS)

JWTs signed with RS256 use asymmetric cryptography: the IdP signs tokens with a private key that only it holds, and publishes the corresponding public keys at a well known URL called the JWKS endpoint (JSON Web Key Set). Anyone including kgateway can fetch these public keys and verify that a token was genuinely issued by that IdP and hasn't been altered since.

This means kgateway never needs to call back to your IdP on every request. It fetches the JWKS once, caches the keys, and verifies signatures locally at the data plane making validation fast and offline capable.

Why this matters for multi-issuer setups: Each IdP has its own JWKS endpoint and its own signing keys. kgateway can hold keys from multiple providers simultaneously, matching each incoming token to the right key by checking its iss claim first.


What you'll build

An HTTPRoute on /api that:

  • Accepts RS256-signed JWTs from Auth0 and Google
  • Enforces aud: my-api on tokens from both providers
  • Forwards the sub and email claims as X-User-Id and X-User-Email headers to your upstream service

Before you begin

  • kgateway ≥ 1.2 installed in your cluster
  • kubectl access with permissions to create custom resources
  • An Auth0 tenant with an API audience configured
  • A Google OAuth 2.0 client ID

How kgateway validates JWTs

Validation happens in the Envoy data plane before a request ever reaches your upstream. On each request, kgateway:

  1. Extracts the bearer token from the Authorization: Bearer <token> header (configurable to cookies or query params).
  2. Resolves the matching issuer by comparing the token's iss claim against each issuer declared in JWTPolicy.spec.providers. The first match wins.
  3. Fetches and caches JWKS from the provider's jwks_uri. Keys are cached per the cacheDuration you set and never re-fetched mid-request.
  4. Validates claims and signature verifies exp, nbf, aud, and the cryptographic signature. Any failure returns 401 Unauthorized immediately.
  5. Forwards claims as headers injects declared claims into request headers so your upstream can make authorization decisions without reparsing the JWT.

Step 1: Create the JWTPolicy

The JWTPolicy is a namespace scoped custom resource that declares which issuers to trust, where to fetch their public keys, and which claims to forward upstream. Create a file named jwt-policy.yaml:

apiVersion: gateway.kgateway.dev/v1alpha1
kind: JWTPolicy
metadata:
  name: multi-issuer-policy
  namespace: default
spec:
  providers:

    # Provider 1: Auth0 tenant
    - name: auth0
      issuer: https://my-tenant.auth0.com/     # note the trailing slash
      audiences:
        - my-api
      remoteJwks:
        uri: https://my-tenant.auth0.com/.well-known/jwks.json
        cacheDuration: 10m
      claimsToHeaders:
        - claim: sub
          header: X-User-Id
        - claim: email
          header: X-User-Email

    # Provider 2: Google
    - name: google
      issuer: https://accounts.google.com      # no trailing slash
      audiences:
        - my-api
      remoteJwks:
        uri: https://www.googleapis.com/oauth2/v3/certs
        cacheDuration: 5m
      claimsToHeaders:
        - claim: sub
          header: X-User-Id
        - claim: email
          header: X-User-Email

Enter fullscreen mode Exit fullscreen mode

⚠️ Issuer strings must be exact. The issuer field is compared character-for-character against the token's iss claim. Auth0 includes a trailing slash in its tokens; Google does not. A mismatch here means every token from that provider will be rejected, even if the signature is valid.


Step 2: Attach the policy to your HTTPRoute

Reference the policy via an annotation on your HTTPRoute. You do not need to modify the route's rules:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-route
  namespace: default
  annotations:
    gateway.kgateway.dev/jwt-policy: multi-issuer-policy
spec:
  parentRefs:
    - name: my-gateway
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api
      backendRefs:
        - name: my-service
          port: 8080

Enter fullscreen mode Exit fullscreen mode


Step 3: Apply and verify

# Apply both resources
$ kubectl apply -f jwt-policy.yaml -f httproute.yaml

# Confirm the policy is accepted by the control plane
$ kubectl get jwtpolicy multi-issuer-policy \
    -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}'
True

# Test with a valid Auth0 token
$ curl -H "Authorization: Bearer $AUTH0_TOKEN" https://my-gateway/api/health
200 OK

# Test rejection: no token → 401
$ curl https://my-gateway/api/health
401 Unauthorized

# Confirm upstream receives the forwarded headers
$ kubectl logs deploy/my-service | grep X-User-Id
X-User-Id: user_123

Enter fullscreen mode Exit fullscreen mode

⚠️ JWKS caching on first request: kgateway fetches JWKS the first time a token from a given issuer arrives. If the jwks_uri is unreachable at that moment, the request fails with 503. Use a cacheDuration of at least 5m in production never 0s outside of development.


Claim validation reference

Claim Validated automatically Notes
iss Yes Must exactly match a declared provider's issuer. First match wins; no fallback.
aud Yes, if configured Token must contain at least one value from the audiences list. Omit the field to skip audience validation (not recommended in production).
exp Yes Expired tokens are rejected with 401. Clock skew tolerance is 60 s by default.
nbf Yes Tokens with a future nbf (not-before) are rejected.
sub, email, roles No kgateway does not validate custom claims. Use claimsToHeaders to forward them and enforce access rules in your upstream service.

Next steps

  • Use a local JWKS secret: Mount JWKS as a Kubernetes secret for air gapped or high security environments.
  • Claim based routing: Route requests to different backends based on forwarded claim headers.
  • Full OIDC with Auth0: Add the authorization code flow for browser facing applications.
  • Monitor validation errors: Surface JWT rejection rates in Prometheus and set alerting thresholds.