Create how-to guide to dynamically set upstream based on authenticating consumer.
Use the OIDC plugin to validate the token to sets a header for the consumer. Then Datakit reads the header and sets either the service or the upstream.
# =============================================================================
# Consumer-based upstream routing with Kong
# =============================================================================
# Goal: for a given caller (identified by a token claim), consistently route to
# ONE specific backend among several — decided AFTER the route is matched.
#
# Why this shape:
# Kong's router runs BEFORE auth, so it cannot match on a consumer/claim.
# Instead we keep ONE route -> ONE service, let OIDC validate the token and
# set the credential from a claim, then let Datakit read that credential and
# OVERRIDE the backend in-place. No second route, no header re-routing, no
# loopback / second proxy hop.
#
# Plugin order (all in the `access` phase) — by static plugin priority, OIDC
# runs before Datakit, so no explicit `ordering` is required:
# openid-connect -> validates token, sets credential.id from `client_id` claim
# datakit -> reads kong.client.credential, maps id -> backend, sets it
#
# Two backend-write options are shown:
# VARIANT A (ACTIVE) : kong.service.upstream -> named Upstream entity (POOL)
# keeps load balancing, health checks, retries.
# VARIANT B (COMMENTED): kong.service.target -> single host:port
# bypasses LB, health checks, retries. Simpler, no pool.
# Pick ONE datakit block. They are mutually exclusive on the same route.
#
# Requires: gateway version with DataKit + property/jwt nodes (3.14-era).
# Confirm the OIDC field name `credential_claim` matches your version.
# =============================================================================
_format_version: "3.0"
# -----------------------------------------------------------------------------
# Backends for VARIANT A (set_upstream). Each Upstream is a POOL of targets, so
# load balancing / health checks / retries are preserved on the override.
# (Not used by VARIANT B — there the host:port comes from the Datakit mapping.)
# -----------------------------------------------------------------------------
upstreams:
- name: upstream-a
targets:
- { target: a1.example:443 }
- { target: a2.example:443 } # HA pool
- name: upstream-b
targets:
- { target: b1.example:443 }
- name: upstream-default # fallback for unmapped / missing claim
targets:
- { target: default.example:443 }
services:
- name: claims-api
protocol: https
host: upstream-default # default backend; Datakit overrides per-request
port: 443
routes:
- name: claims-route
paths:
- /v1/claims
plugins:
# -----------------------------------------------------------------------
# 1) OIDC — validate the bearer token and set the credential from a claim.
# `credential_claim` puts the claim value onto credential.id WITHOUT
# requiring a Kong Consumer entity (consumer_optional: true).
# -----------------------------------------------------------------------
- name: openid-connect
config:
issuer: <issuer-url>
auth_methods:
- bearer
credential_claim: # credential.id := value of this claim
- client_id
consumer_optional: true # no Kong Consumer needed; don't 401 on absence
# hide_credentials: true # optional: strip Authorization before upstream
# -----------------------------------------------------------------------
# 2) DataKit — VARIANT A (ACTIVE): route to a named UPSTREAM (pool).
# Runs after openid-connect by static plugin priority (OIDC's priority is
# higher, so it executes first in the access phase; Datakit follows), so
# kong.client.credential is already populated. No `ordering` needed. If
# you add another access-phase plugin, re-verify with X-DataKit-Debug-Trace.
#
# Flow: GET_CREDENTIAL -> PICK_UPSTREAM (jq map) -> SET_UPSTREAM
# -----------------------------------------------------------------------
- name: datakit
config:
# debug: true # enable only while validating; dev-only output
nodes:
# Read the authenticated credential (GET mode: no input connected).
- name: GET_CREDENTIAL
type: property
property: kong.client.credential
# Map credential.id (the claim value) -> Upstream entity name.
# Unmapped callers fall through to "upstream-default".
- name: PICK_UPSTREAM
type: jq
input: GET_CREDENTIAL
jq: |
{
"1234": "upstream-a",
"5678": "upstream-b"
}[.id] // "upstream-default"
# Apply it (SET mode: input connected). Keeps LB / health / retries.
- name: SET_UPSTREAM
type: property
property: kong.service.upstream
input: PICK_UPSTREAM
# -----------------------------------------------------------------------
# 2) DataKit — VARIANT B (ALTERNATIVE): route to a fixed host:port TARGET.
# Bypasses load balancing, health checks, and retries. Use only for
# stable single-host backends. To use this instead of Variant A:
# - comment out the Variant A `datakit` plugin block above
# - uncomment this block
# - the `upstreams:` section becomes optional
#
# - name: datakit
# config:
# nodes:
# - name: GET_CREDENTIAL
# type: property
# property: kong.client.credential
#
# # Map credential.id -> "host:port".
# - name: PICK_TARGET
# type: jq
# input: GET_CREDENTIAL
# jq: |
# {
# "1234": "a1.example:443",
# "5678": "b1.example:443"
# }[.id] // "default.example:443"
#
# # Apply it. NOTE: bypasses balancer (no health checks / retries).
# - name: SET_TARGET
# type: property
# property: kong.service.target
# input: PICK_TARGET
#
# # If backends differ on scheme, also set it:
# # - name: SET_SCHEME
# # type: property
# # property: kong.service.request.scheme
# # input: <jq node returning "https" | "http">
# -----------------------------------------------------------------------
# =============================================================================
# Deploy & verify
# =============================================================================
# deck gateway validate deck.yaml
# deck gateway diff deck.yaml
# deck gateway sync deck.yaml
#
# Smoke test (trace shows node execution order + each node's output):
# curl -i https://<proxy>/v1/claims \
# -H "Authorization: Bearer <token-with-client_id=1234>" \
# -H "X-DataKit-Debug-Trace: true"
#
# Confirm in the trace:
# - GET_CREDENTIAL runs AFTER openid-connect and returns the credential
# - .id carries the claim value (e.g. "1234")
# - PICK_* resolves to the expected backend (not the default)
# - SET_* executes before proxying
# =======================================================================
Jobs to be done
Create how-to guide to dynamically set upstream based on authenticating consumer.
Information
Use the OIDC plugin to validate the token to sets a header for the consumer. Then Datakit reads the header and sets either the service or the upstream.
Size
M