A human runs kubectl. There is no cluster credential on
disk — instead, the kubelogin plugin opens a browser,
the human signs in to Okta (with MFA), and Okta mints an
id_token: a signed assertion about who just authenticated.
That JWT is the only credential kubectl will ever present.
- kubelogin runs the PKCE auth-code flow — browser to
Okta's
/authorize, then a code-for-token exchange at/token. No client secret anywhere. - Okta signs the id_token — its claims (
email,groups,amr) describe exactly who authenticated and how.
# ~/.kube/config — user entry
users:
- name: okta
user:
exec:
command: kubectl
args:
- oidc-login
- get-token
- --oidc-issuer-url=https://dev-12345678.okta.com
- --oidc-client-id=0oa8q2lmv3RtYwHcK5d7
- --oidc-extra-scope=email,groups # without groups: thin token
the client_id and scopes you request become the
token's aud and groups
Header
algRS256kidx9KqLmA4uVrP0eT2sBn6CwDyEfGhJiKlMnOpQrStUvWtypJWT
Payload
| Claim | Value |
|---|---|
iss | https://dev-12345678.okta.com |
aud | 0oa8q2lmv3RtYwHcK5d7 |
sub | 00u5fj9qx2TnWbVrA5d7 |
email | lab-admin@authlab.app |
groups | ["app-k8s-admins"] |
amr | ["pwd","mfa","google_otp"] |
iat | 1778600000 |
exp | 1778603600 |
jti | ID.bT4mWx9KqLpHcAe2sFnVuD7gYiR0oZ3NhCkM |
Compact Serialization
eyJhbGciOiJSUzI1NiIsImtpZCI6Ing5S3FMbUE0dVZyUDBlVDJzQm42Q3dEeUVmR2hKaUtsTW5PcFFyU3RVdlciLCJ0eXAiOiJKV1QifQeyJpc3MiOiJodHRwczovL2Rldi0xMjM0NTY3OC5va3RhLmNvbSIsImF1ZCI6IjBvYThxMmxtdjNSdFl3SGNLNWQ3Iiwic3ViIjoiMDB1NWZqOXF4MlRuV2JWckE1ZDciLCJlbWFpbCI6ImxhYi1hZG1pbkBhdXRobGFiLmFwcCIsImdyb3VwcyI6WyJhcHAtazhzLWFkbWlucyJdLCJhbXIiOlsicHdkIiwibWZhIiwiZ29vZ2xlX290cCJdLCJpYXQiOjE3Nzg2MDAwMDAsImV4cCI6MTc3ODYwMzYwMCwianRpIjoiSUQuYlQ0bVd4OUtxTHBIY0FlMnNGblZ1RDdnWWlSMG9aM05oQ2tNIn0anonymized-signature-not-valid-for-cryptographic-use
What must match
The kube-apiserver validates these top-down; a miss on any one is a hard 401. Hover a claim for why.
iss aud groups Where the other side lives
In the workload track, the target column was a cloud resource. Here, for the first time, it is a config file on the consumer itself — apiserver flags on the control-plane node.
Step 2 presents this token to an apiserver — once without that config, once with it.