Skip to content

BuildNot yet confirmed elsewhere1 publisher3 min readPublished

ALB validates the JWT, Keycloak decides what is in it: the Terraform for the matching realm

A dev.to walkthrough publishes the Keycloak stack behind an EKS gateway auth setup: one enabled grant, four redirect URLs, and a group membership Terraform does not fully own.

The Engineer · Build desk

How we use AISend a correction

Photograph accompanying ALB validates the JWT, Keycloak decides what is in it: the Terraform for the matching realm
Photo: dev.to

What happened

  • A follow-up to an ALB Gateway API JWT-offload experiment publishes the Terraform/OpenTofu stack that configures Keycloak as the matching OIDC provider for EKS.
  • The cluster-facing client is confidential with a client secret, and its authorization code flow is the only grant left switched on.
  • A kube-api client scope holds the protocol mappers, and the one shown copies the username attribute into both the access token and the ID token.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint There is no scripted way to mint a cluster token here: a human has to finish a browser redirect to a loopback port or a Postman callback, which is why automation needs a separate federated client...
  • exposure Declaring group membership non-exhaustively means anyone dropped into kube-admin through the console keeps that grant through every future apply, and no plan output will surface it.
  • decision Because claim names are decided in the realm's client scope, the team writing cluster RBAC has to settle the claim contract with the realm owner before either side can ship independently.
  • contradiction The prose says the client role reaches the token's role claim, but the mappers actually published stop at username, so copying this stack verbatim yields an accepted token with nothing to...

The gateway checks a signature and looks for claims. It cannot add one. So the useful part of this stack is not the ALB policy but the client scope, which the author describes as the shared set of protocol mappers attached to the client [12]: whatever is not mapped there does not exist as far as the cluster is concerned.

The narrowing starts with the client flags. On the `kube-api` client, one flow toggle is on, `standard_flow_enabled`, which the author identifies as the authorization code grant [5]. Six are off: implicit, direct access grants, service accounts, standard token exchange, the OAuth2 JWT authorization grant and device authorization [6][18]. `full_scope_allowed = false` limits the token to roles scoped to this client [7]. With the direct access grant disabled, there is no password-grant shortcut for a test token; anything the gateway sees has to come back through one of the four registered redirect URLs [8][19], two loopback ports for kubelogin [9] and two Postman callbacks [10]. That constraint is exactly why runners need their own path, and the author's JWT-federated GitHub Actions client is deferred to a future article [3].

The membership side is looser than the client side. `keycloak_user_groups` is declared with `exhaustive = false` [13], and the `kube-admin` group carries the client role named `admin`, described in the code as the cluster-admin role for kube-api [14][11]. Non-exhaustive means Terraform owns the rows it declares and ignores the rest, so a hand-added member of that group is not drift anyone will be shown.

Two other lines are worth reading as policy rather than plumbing. `email_verified = true` is set from config for every user the stack creates [15], which is the realm asserting a fact about a person rather than the person proving it. And `lifecycle { ignore_changes = [first_name, last_name] }` [16] hands display names back to the directory while leaving `username` under Terraform control, which matters because `username` is the one attribute the article shows being copied into both the access token and the ID token [17][20]. Rename it in config and you rename the subject the cluster authorizes.

Which is where the walkthrough stops short of its own promise. The role resource is said to appear in the token's role claim once assigned to a user [11], but the mapper set published here contains only the username mapper, and the text breaks off mid-explanation of `add_to_id_token` [22][23]. Follow it literally and you get a token the gateway will accept carrying an identity and no authorization data.

One last asymmetry. The provider itself authenticates with a service-account client using the client credentials grant, driven by five environment variables and an otherwise empty `provider "keycloak" {}` block [2]. Service accounts are precisely what the `kube-api` client forbids [6][21]. The realm-management credential and the human credential are different clients with opposite capabilities, which is correct, and also means the secret that can rewrite every role mapping in this stack lives wherever the plan runs.

What to watch

  • The promised write-up of the GitHub Actions JWT-federated client, currently the only identity path in this stack with no configuration shown.
  • Whether group membership moves to exhaustive ownership, which decides the fate of any kube-admin member added by hand in the console.
  • Which mapper is published alongside username in the kube-api client scope, and whether the gateway policy reads the access token or the ID token.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence66
Adoption12
Hype gap−8
Incentives28
Confidence58
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    The author previously tested using ALB CRDs to offload JWT validation for OIDC-based authentication to the Amazon EKS Kubernetes API, and this article supplies the Terraform/OpenTofu stack that sets up Keycloak as the OIDC provider.

    ReportedSupportedView cited source
  2. [2]

    The official Keycloak Terraform provider is configured with a service-account client (OAuth 2.0 client credentials grant) and five environment variables (KEYCLOAK_URL, KEYCLOAK_REALM, KEYCLOAK_BASE_PATH, KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET), so an empty provider "keycloak" {} block is enough.

    ReportedSupportedView cited source
  3. [3]

    For GitHub Actions runners the author used a JWT-federated client and says it deserves a dedicated article.

    ReportedSupportedView cited source

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · August 22, 2026

    OIDC: Keycloak setup for ALB Gateway API

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Topics

  • Kubernetes API OIDC AuthenticationFollow
  • Keycloak Realm and Client ConfigurationFollow
  • OAuth 2.0 Grant and Redirect HardeningFollow
  • Identity as Code with Terraform/OpenTofuFollow

Entities

Loading related stories