Auth methods: OIDC and Kubernetes
Humans via SSO, workloads via service accounts.
In short. Vault auth with OIDC and Kubernetes: humans via SSO, workloads via short-lived service-account tokens.
Think of Vault as a secured building with two entrances. Humans walk in through the front lobby, where the badge reader is wired straight into your corporate identity provider — there is no separate Vault password to remember or leak, just the SSO login your engineers already have. Workloads use the loading dock around back, where the only ID that counts is the service account token the pod was born with. Same building, two very different doors, and each needs a different lock. This lesson wires up both: OIDC for people, Kubernetes auth for machines.
The front door: OIDC for humans
OIDC auth lets Vault delegate the question "who are you?" to an identity provider you already trust — Okta, Entra ID, Google, Keycloak. Vault never sees a password. It redirects the browser to the IdP, receives a signed ID token back, and reads claims out of it. You bind a role to the IdP's client ID and tell Vault which claim uniquely identifies the person (user_claim) and which claim carries their group membership (groups_claim). The allowed_redirect_uris must list both the CLI's localhost callback and your Vault UI address, exactly — scheme, port, and path included. Get one character wrong and the IdP refuses the handoff before Vault ever sees a token. Because the Vault token that comes out is short-lived, the human re-authenticates through SSO when it expires, so disabling their account upstream cuts off Vault on its own.
# Turn on the OIDC auth methodvault auth enable oidc# Point Vault at your identity providervault write auth/oidc/config \oidc_discovery_url="https://login.example.com" \oidc_client_id="$OIDC_CLIENT_ID" \oidc_client_secret="$OIDC_CLIENT_SECRET" \default_role="engineer"# A role: which claims to trust, where to redirect, what token to mintvault write auth/oidc/role/engineer \user_claim="sub" \groups_claim="groups" \oidc_scopes="openid,profile,groups" \bound_audiences="$OIDC_CLIENT_ID" \allowed_redirect_uris="http://localhost:8250/oidc/callback" \allowed_redirect_uris="https://vault.example.com/ui/vault/auth/oidc/oidc/callback" \token_policies="engineer" \token_ttl=1h# Engineers now log in with: vault login -method=oidc role=engineer
Turning SSO groups into Vault policy
Granting policy to individual users does not scale and drifts the moment someone changes teams. Instead, let group membership in your IdP drive Vault authorization. When a user logs in through OIDC, Vault reads the groups_claim and looks for a matching group alias. You pre-create an external identity group, attach the policies you want it to carry, and map the IdP's group name to it through a group alias keyed on the OIDC mount accessor. Now a person added to platform-eng in Okta inherits the platform policy on their very next login, and removing them there revokes it — no Vault change, no ticket, no stale grant left behind. Membership lives in exactly one system, and Vault follows it. The alias name must match the string your IdP actually emits in the groups claim, which is often a group ID rather than a display name.
# Grab the accessor of the OIDC mountACCESSOR=$(vault auth list -format=json | jq -r '."oidc/".accessor')# External group that carries the policyvault write identity/group name="platform-eng" \type="external" \policies="platform"GROUP_ID=$(vault read -field=id identity/group/name/platform-eng)# Alias ties the IdP's group name to the Vault groupvault write identity/group-alias \name="platform-eng" \mount_accessor="$ACCESSOR" \canonical_id="$GROUP_ID"
The loading dock: Kubernetes auth for workloads
Machines cannot do a browser redirect, so workloads authenticate with the one credential Kubernetes already hands every pod: its projected service account token. Vault takes that JWT, calls the cluster's TokenReview API to confirm it is genuine and unexpired, and — if the pod's service account name and namespace match a role's bindings — issues a Vault token in return. There is no secret to distribute and no static credential to rotate; the identity is the pod's own. Since Kubernetes 1.21 these tokens are time-bound and audience-scoped, so one scraped from a pod cannot be replayed forever or against a different audience. Bind each role narrowly to a service account name and namespace, so only the webapp pods in prod can ever assume the webapp policy — a compromised pod in another namespace gets nothing.
vault auth enable kubernetes# In-cluster: you supply the host; Vault defaults to its own pod's# service account token and the local cluster CA to reach the APIvault write auth/kubernetes/config \kubernetes_host="https://kubernetes.default.svc:443"# Only the 'webapp' SA in 'prod' may assume this rolevault write auth/kubernetes/role/webapp \bound_service_account_names="webapp" \bound_service_account_namespaces="prod" \token_policies="webapp" \token_ttl=1h# From inside the pod, exchange the SA token for a Vault tokenTOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)vault write auth/kubernetes/login role=webapp jwt="$TOKEN"