Workload identities
Read the Identity overview first for the IdentityProvider
resource, userInfoPrefix, and claim mappings.
Not every caller of the Hub API is a human. A workload identity is a machine caller — a CI job, a pipeline, an agent, a ServiceAccount — that Hub authenticates the same way it authenticates a person, from claims in a JWT.
This page covers what a workload's Hub username looks like and how it trades its native credential for a Hub token. To grant it permissions once it has one, see Workload identities in access management.
Two kinds
Hub recognizes two kinds of workload identity. They differ only in where the identity comes from and what its username looks like.
Control plane ServiceAccounts
A Kubernetes ServiceAccount inside a managed control plane, authenticated through the control plane's own connection to Hub. Its Hub username has the shape:
upbound:hub:controlplane:<realm-name>:<control-plane-name>:system:serviceaccount:<namespace>:<sa-name>
The my-job ServiceAccount in the apps namespace of the prod control plane
in the acme realm therefore reaches Hub as:
upbound:hub:controlplane:acme:prod:system:serviceaccount:apps:my-job
OIDC workload identities
Any workload holding a JWT from a registered
IdentityProvider — a GitHub Actions job, a GitLab CI pipeline, a cloud
workload-identity-federated service, or a ServiceAccount in a cluster Hub
doesn't manage. These get a username from the provider's claim mappings exactly
as a human does: the username comes from claimMappings.username.claim, groups
from claimMappings.groups.claim, and both carry the provider's
userInfoPrefix.
GitHub Actions is the common case. Register GitHub's OIDC issuer as an
IdentityProvider and a workflow's token maps to whichever claim you point
claimMappings.username.claim at. GitHub's sub claim identifies both the
repository and the ref that triggered the run, so with userInfoPrefix: "github:" a job on the main branch of acme/infra reaches Hub as:
github:repo:acme/infra:ref:refs/heads/main
Prefer the narrowest claim your provider offers. A sub that encodes both
repository and ref lets you grant production access to a main-branch pipeline
without granting it to every pull request build in the same repository.
Confirm the resolved username
Don't construct a workload's username by hand — read it back from Hub. Have the workload run:
kubectl auth whoami
See Verifying your identity for how to read the output. A role binding whose subject name is off by one character silently grants nothing, so this is worth doing before you write the binding.
Getting a Hub token
Neither kind of workload sends its native credential to Hub's data APIs. Both trade it for a Hub token first, through the token exchange endpoint.
Use hub-credential-helper rather than driving the exchange by hand. Point it
at the file holding the workload's JWT — a projected ServiceAccount token, a
GitHub Actions ID token, or any other IdP-issued JWT — and it exchanges,
caches, and prints the Hub token:
hub-credential-helper get-token \
--hub-url=https://hub.example.com \
--token-file=/var/run/secrets/token
The helper also works as a kubectl credential plugin, so a job can run
kubectl against Hub with no explicit token handling. See kubectl
login for the kubeconfig wiring.
The raw exchange
If you can't run the helper, the endpoint accepts the standard RFC 8693 form directly:
POST /apis/tokenexchange.hub.upbound.io/v1alpha1/tokenexchangerequests
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=<your IdP-issued JWT>
&subject_token_type=urn:ietf:params:oauth:token-type:jwt
The subject token must be issued by an IdentityProvider currently registered
with Hub, and its aud must match one of that provider's configured
issuer.audiences. The returned Hub token carries the same subject and group
claims as the input, after userInfoPrefix is applied, but is signed by Hub's
own key.
Related resources
- Workload identities in access management — binding roles to the identities described here.
- Identity overview — claim mappings and
userInfoPrefix. - kubectl login — using the same credentials from
kubectl. - CLI and AI agent login — the interactive counterpart for humans at a terminal.