📚Book Signing at KubeCon EU 2026Meet us at Booking.com HQ (Mon 18:30-21:00) & vCluster booth #521 (Tue 24 Mar, 12:30-1:30pm) — free book giveaway!RSVP Booking.com Event
Security advanced ⏱ 15 minutes K8s 1.28+

K8s OIDC Authentication Login Guide

Configure OIDC authentication for Kubernetes API server. --enable-oidc-issuer with GKE, Keycloak, Dex, kubelogin plugin, and RBAC SSO integration.

By Luca Berton 📖 5 min read

💡 Quick Answer: Configure OpenID Connect authentication for Kubernetes API server. Keycloak, Dex, Google OIDC, kubelogin plugin, and RBAC integration for SSO.

The Problem

Managing individual client certificates for every cluster user doesn’t scale, can’t enforce your org’s password/MFA policy, and gives you no way to instantly revoke access when someone leaves. You need the API server to trust your existing identity provider instead.

The Solution

The OIDC Login Flow

sequenceDiagram
    participant U as User / kubectl
    participant IDP as Identity Provider<br/>Keycloak / Okta / Azure AD
    participant API as Kubernetes API Server
    U->>IDP: 1. Request token (login)
    IDP->>U: 2. ID Token (JWT)
    Note over U: kubectl stores token via kubeconfig exec plugin
    U->>API: 3. API request + token
    Note over API: 4. Validate signature & claims via --oidc-* flags<br/>5. Extract username/groups<br/>6. RBAC authorization as usual

Configure the API Server

# /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
  containers:
    - name: kube-apiserver
      command:
        - kube-apiserver
        - --oidc-issuer-url=https://keycloak.example.com/realms/kubernetes
        - --oidc-client-id=kubernetes
        - --oidc-username-claim=preferred_username
        - --oidc-username-prefix=oidc:
        - --oidc-groups-claim=groups
        - --oidc-groups-prefix=oidc:
        - --oidc-ca-file=/etc/kubernetes/pki/oidc-ca.crt
        - --oidc-required-claim=aud=kubernetes

For managed clusters, this is set via the provider’s API instead of raw flags:

# EKS
aws eks update-cluster-config --name my-cluster --identity-provider-config '{
  "oidc": {"identityProviderConfigName": "keycloak",
    "issuerUrl": "https://keycloak.example.com/realms/kubernetes",
    "clientId": "kubernetes", "usernameClaim": "preferred_username",
    "usernamePrefix": "oidc:", "groupsClaim": "groups", "groupsPrefix": "oidc:"}
}'

Bind OIDC Groups to RBAC

Use group-based bindings, not per-user — groups are managed in the IdP, so access changes there without touching cluster config:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: oidc-cluster-admins
subjects:
  - kind: Group
    name: oidc:cluster-admins   # matches the prefix set by --oidc-groups-prefix
    apiGroup: rbac.authorization.k8s.io
roleRef: {kind: ClusterRole, name: cluster-admin, apiGroup: rbac.authorization.k8s.io}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata: {name: oidc-developers, namespace: production}
subjects:
  - {kind: Group, name: "oidc:developers", apiGroup: rbac.authorization.k8s.io}
roleRef: {kind: ClusterRole, name: edit, apiGroup: rbac.authorization.k8s.io}

Configure kubectl with kubelogin

Kubernetes doesn’t refresh OIDC tokens itself — kubelogin (the oidc-login kubectl plugin) handles the browser login and token cache:

brew install int128/kubelogin/kubelogin   # macOS
# ~/.kube/config
users:
  - name: oidc-user
    user:
      exec:
        apiVersion: client.authentication.k8s.io/v1beta1
        command: kubectl
        args:
          - oidc-login
          - get-token
          - --oidc-issuer-url=https://keycloak.example.com/realms/kubernetes
          - --oidc-client-id=kubernetes
          - --oidc-client-secret=kubernetes-client-secret
          - --oidc-extra-scope=groups

Provider-Specific Notes: Azure AD and Okta

# Azure AD API server flags
- --oidc-issuer-url=https://login.microsoftonline.com/<tenant-id>/v2.0
- --oidc-client-id=<application-id>
- --oidc-username-claim=email
- --oidc-groups-claim=groups
# Okta API server flags
- --oidc-issuer-url=https://your-org.okta.com
- --oidc-client-id=<okta-client-id>
- --oidc-username-claim=email
- --oidc-username-prefix=okta:

Azure AD in particular needs the kubelogin get-token exec plugin (not oidc-login) with --environment/--server-id/--tenant-id — see the OIDC claims-mapping troubleshooting guide if your IdP’s actual claim names don’t match what you configured.

Verification

kubectl oidc-login setup --oidc-issuer-url=... --oidc-client-id=kubernetes --oidc-client-secret=...
kubectl auth whoami
kubectl auth can-i --list

# Impersonate to test a binding without that user's actual token
kubectl auth can-i create pods --as=oidc:jane@example.com -n production
kubectl auth can-i delete deployments --as-group=oidc:developers -n production

Common Issues

IssueCauseFix
Token validation failsIssuer URL doesn’t exactly match, or clock skew between API server and IdPCopy the issuer URL verbatim from the IdP’s discovery document; sync node clocks (NTP)
Groups claim missing from tokenGroup mapper not configured in IdP, or groups only added to the access token, not the ID tokenKubernetes reads the ID token — confirm the IdP includes groups there, not just the access token
kubectl auth errors after IdP password changeStale cached tokenrm -rf ~/.kube/cache/oidc-login and log in again
RBAC binding has no effectGroup name in the binding doesn’t include the --oidc-groups-prefixClusterRoleBinding subject must be oidc:developers, not developers, if the prefix is oidc:

Best Practices

  • Bind RBAC to groups, not individual users — access changes happen in the IdP, not in cluster YAML
  • Use username/group prefixes (oidc:, okta:) to avoid collisions with local/service-account identities of the same name
  • Set token lifetimes to 1-8 hours — short enough to limit blast radius from a leaked token, long enough to not annoy users
  • Enable PKCE for the OIDC client — mandatory for public clients doing the CLI login flow
  • Audit OIDC authentication events at Metadata level minimum, Request level for privileged groups
  • Test the full login → RBAC flow in staging before rolling out to a production cluster’s only auth path

Key Takeaways

  • OIDC lets users authenticate with existing corporate credentials instead of per-user client certificates
  • The API server validates tokens via --oidc-* flags — it never talks to the IdP directly at request time, only at flag-configured issuer/CA validation
  • Kubernetes doesn’t handle token refresh — kubelogin/oidc-login kubectl plugins manage that via kubeconfig exec
  • Bind RBAC to OIDC groups (with the configured prefix), not individual users, for maintainable access control
  • If claim names don’t match between your IdP and your --oidc-* flags, see the dedicated claims-mapping troubleshooting guide
#oidc #authentication #sso #keycloak
Luca Berton
Written by Luca Berton

Principal Solutions Architect specializing in Kubernetes, AI/GPU infrastructure, and cloud-native platforms. Author of Kubernetes Recipes and creator of CopyPasteLearn courses.

Kubernetes Recipes book cover

Want More Kubernetes Recipes?

This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.

Luca Berton Ansible Pilot Ansible by Example Open Empower K8s Recipes Terraform Pilot CopyPasteLearn ProteinLens