Skip to main content

Verifying your identity

Suggested reading

Read the Identity overview first for the IdentityProvider resource, userInfoPrefix, and claim mappings — this page shows you what those settings produced for a real caller.

Role bindings match on exact strings, so the first question to answer when access doesn't behave is who does Hub think I am? Ask Hub rather than deriving the answer from your IdentityProvider config: a binding whose subject name is off by one character grants nothing, silently.

kubectl auth whoami

The quickest check. It posts a SelfSubjectReview under the hood and prints the two fields role bindings match against:

$ kubectl auth whoami
ATTRIBUTE VALUE
Username entra:alice@example.com
Groups [entra:platform-eng system:authenticated]

Check three things:

  • The username matches <userInfoPrefix><username-claim-value> (entra:alice@example.com in the example).
  • Every group you expect appears, prefixed with userInfoPrefix.
  • If groups are missing, the IdP either didn't emit a groups claim or you pointed claimMappings.groups.claim at the wrong claim name.

This needs kubectl wired up against Hub — see kubectl login.

The request and response

SelfSubjectReview is the standard Kubernetes authentication.k8s.io/v1 resource, cluster-scoped and create-only. Hub ignores the request body entirely and answers from the bearer token you authenticated with, so the review can't be made to describe anyone but the caller:

POST /apis/authentication.k8s.io/v1/selfsubjectreviews
Authorization: Bearer <hub-token>
Content-Type: application/json

{
"apiVersion": "authentication.k8s.io/v1",
"kind": "SelfSubjectReview"
}

The response carries the resolved identity under status.userInfo:

{
"apiVersion": "authentication.k8s.io/v1",
"kind": "SelfSubjectReview",
"status": {
"userInfo": {
"username": "entra:alice@example.com",
"groups": [
"entra:platform-eng",
"system:authenticated"
],
"extra": {
"authentication.hub.upbound.io/identity-provider": ["entra"],
"authentication.hub.upbound.io/issuer": ["https://login.microsoftonline.com/<tenant-id>/v2.0"],
"authentication.hub.upbound.io/sub": ["8f14e45f-ceea-467a-9a1c-2b3d4e5f6a7b"],
"authentication.hub.upbound.io/email": ["alice@example.com"],
"authentication.hub.upbound.io/is-human": ["true"],
"authentication.hub.upbound.io/expiry": ["2026-07-31T16:04:11Z"],
"upbound.io/org": ["acme"]
}
}
}
}

username and groups are the values every role binding matches against — already carrying the userInfoPrefix from the IdentityProvider that authenticated you. kubectl auth whoami renders exactly these two fields.

To issue the request without kubectl:

curl -s -X POST "$HUB_URL/apis/authentication.k8s.io/v1/selfsubjectreviews" \
-H "Authorization: Bearer $(hub-credential-helper get-token --hub-url=$HUB_URL)" \
-H "Content-Type: application/json" \
-d '{"apiVersion":"authentication.k8s.io/v1","kind":"SelfSubjectReview"}'

Every authenticated principal may create a SelfSubjectReview, regardless of role — it's part of the built-in system:basic-user grant.

The extra attributes

The extra map carries the identity attributes Hub derived while minting your token. Each value is a list, per the Kubernetes UserInfo schema, even when only one value is meaningful:

KeyCarries
authentication.hub.upbound.io/identity-providerName of the IdentityProvider that authenticated you
authentication.hub.upbound.io/issuerThe upstream OIDC issuer URL
authentication.hub.upbound.io/subThe sub claim from the upstream token
authentication.hub.upbound.io/emailThe verified email address
authentication.hub.upbound.io/is-human"true" for browser and device-flow logins, "false" for exchanged workload tokens
authentication.hub.upbound.io/expiryToken expiry, RFC 3339
upbound.io/orgThe organization the token was minted for

uid is present in the schema but Hub doesn't populate it — identity is keyed on the prefixed username, not on an opaque ID.

When the identity is right but access still isn't

SelfSubjectReview answers who am I. When the answer looks correct and access still doesn't behave, the question becomes what am I allowed to do, which is a different endpoint: see Filtering and self-review for SelfSubjectAccessReview and how to read the filter it returns.