Configure an IdentityProvider
Read the Identity overview first for what Hub identifies and how tokens reach it. To register your first provider as part of a Helm install, see Installing Hub, which generates a provider from a handful of values.
An IdentityProvider allows you to configure which identity systems the Hub can
trust. The IdentityProvider represents one OIDC issuer in the
iam.hub.upbound.io API group. It carries both the OAuth2 redirect
parameters for the login flow and the token-validation rules Hub applies to
every bearer token.
A minimal provider
A complete IdentityProvider for a standards-compliant issuer that humans log
in through looks like this:
apiVersion: iam.hub.upbound.io/v1beta1
kind: IdentityProvider
metadata:
name: entra
spec:
redirect:
browserLogin: true
clientSecret: "<client-secret>" # encrypted at rest; reads return "***"
scopes:
- openid
- email
- profile
validation:
issuer:
url: https://login.microsoftonline.com/<tenant-id>/v2.0
audiences:
- <client-id>
claimMappings:
username:
# A user with email alice@example.com becomes "entra:alice@example.com".
claim: email
groups:
# A group value "platform-eng" becomes the Hub group "entra:platform-eng".
claim: groups
extra:
- key: authentication.hub.upbound.io/email
valueExpression: claims.email
claimValidationRules:
- expression: "claims.email_verified == true"
message: "email must be verified"
userValidationRules:
- expression: "user.username.endsWith('@example.com')"
message: "email domain not permitted"
Apply it through the API, or add it to hub-core.bootstrap.files for
GitOps-style management. hub-auth reconciles IdentityProviders from that
directory. See Keep credentials in a Secret
to avoid putting the client secret in the file.
Key knobs
userInfoPrefix. Prepended to every value Hub reads from the username, group, and extras claims. It must equal the provider'smetadata.namefollowed by a colon (entra:for a provider namedentra), and Hub fills in that value when you omit it. Choose the provider name with this in mind. The prefix may not overlap the reservedsystem:orupbound:namespaces, which is what stops a provider from claimingsystem:masters.claimMappings.usernameandclaimMappings.groups. Select which claims in the token become the Hub username and group list.usernamedefaults to thesubclaim; set it explicitly when you want a readable username such asemail.groupsis optional but recommended. Without it you can only bind roles to individual users.claimValidationRulesanduserValidationRules. CEL expressions Hub evaluates on every token. Failed rules reject the token. Common uses: requireemail_verified == true, restrict to a single email domain, or block reserved group prefixes.redirect.browserLogin. At most oneIdentityProvidermay set this totrue. That provider drives the Hub UI login redirect when you register multiple IdPs.redirect.prompt. The OIDCpromptparameter Hub sends on the login redirect. When unset, Hub sendsprompt=consent, so providers that issue a refresh token only at first consent, such as Google, keep issuing one. Set it to""to omit the parameter, which stops a provider with consent already granted (for example Entra ID with org-wide admin consent) from showing a consent screen on every login.login,select_account, or a space-separated combination are sent as-is. Hub rejectsnone.
Issuer options
validation.issuer takes these optional fields alongside url and
audiences:
| Field | Purpose |
|---|---|
backendIssuerURL | In-cluster base URL of the provider. Hub rewrites any request that starts with the public issuer URL (discovery, JWKS, and token endpoint calls) to use this URL instead. Use it when the issuer URL in tokens isn't reachable from inside the cluster. |
certificateAuthority | PEM CA bundle for TLS connections to the discovery and JWKS endpoints. Defaults to the system root pool. |
jwks | Inline JSON Web Key Set. Hub verifies tokens against these keys and skips discovery and network JWKS fetching. url must still match the iss claim. |
audienceMatchPolicy | How audiences is matched against the token's aud claim: MatchAny (default) or MatchAllClaimsInTokenAudience. |
Fields you can't change later
These fields are immutable, so getting them wrong means deleting and recreating the provider:
validation.userInfoPrefix. Every role binding written against the old prefix stops matching, so a change is a migration. See Multiple providers.validation.issuer.url. Must equal theissclaim in issued tokens verbatim, including scheme, host, and path. Omit any trailing slash.
Hub also reserves certain metadata.name values: the names upbound, ctp,
and space, and anything starting with upbound-, ctp-, or space-, for
providers it creates itself. Unrelated names that merely begin with the same
letters, such as spacex, are fine.
Defaults worth knowing
redirect.scopesis required whenever you setredirect, and must includeopenid. Hub rejects a provider without it.["openid", "email", "profile"]is the usual set.- A
subclaim is always required. Hub prepends a claim-validation rule of its own demanding a non-empty stringsub, regardless of what you map the username to. Hub rejects a token without one before your own rules run.
The redirect URI
The IdentityProvider doesn't carry a redirect URI. hub-auth, the service
that runs the login flow, composes the callback from its externally reachable
base URL (<base-url>/oidc/callback), so the URI you register with your
provider has to match that host verbatim. On a Helm install the base URL is
hub-auth.auth.authExternalURL, falling back to hub-auth.auth.externalURL
when that's empty. The install guide sets hub-auth.auth.externalURL to the
same URL as global.hubCoreExternalUrl. See Installing
Hub.
Keep credentials in a Secret
A provider in the bootstrap directory can read its client ID, client secret,
and issuer URL from a Kubernetes Secret instead of carrying them inline. Add
an annotation whose value is <secret-name>/<key> for each field:
| Annotation | Fills |
|---|---|
bootstrap.hub.upbound.io/client-id-from | spec.validation.issuer.audiences[0], the client ID the redirect sends. Any further audiences stay as written. |
bootstrap.hub.upbound.io/client-secret-from | spec.redirect.clientSecret |
bootstrap.hub.upbound.io/issuer-url-from | spec.validation.issuer.url |
apiVersion: iam.hub.upbound.io/v1beta1
kind: IdentityProvider
metadata:
name: entra
annotations:
bootstrap.hub.upbound.io/client-id-from: entra-oidc/client-id
bootstrap.hub.upbound.io/client-secret-from: entra-oidc/client-secret
spec:
# ...as above, without clientSecret or the first audience
hub-auth mounts only the Secrets you list in
hub-auth.auth.bootstrap.credentialSecretRefs, so add each one there. Login
fails for a provider whose Secret isn't listed. hub-auth can read every key in
a listed Secret, so keep those Secrets limited to OIDC values. Rotated values
take effect within a few minutes.
What browser login does
Registering the provider above wires up the flow below. Understanding it helps for two reasons: it shows why the redirect URI has to match, and it shows where the IdP stays in the loop after the initial sign-in.
The following properties of this flow matter operationally:
- Neither the IdP's ID token nor its refresh token reaches the browser.
They live on the session row. The browser holds only
hub_sid, an opaque HMAC-signed handle, andhub_token, the short-lived Hub JWT. Both areHttpOnly, so page JavaScript never sees either. - Login and token exchange run in a separate service. hub-auth (port 8444 by default) accepts session cookies and IdP tokens. hub-core, which serves the main API, accepts only Hub-signed JWTs. That keeps the fast verification path free of database lookups.
- The IdP stays in the loop. Hub extends a session by redeeming the stored refresh token, not by minting a long-lived credential of its own. See Revocation.
Logging out at /oidc/logout deletes the session, clears both cookies, and,
when the IdP advertises an end_session_endpoint, redirects through it with
id_token_hint, so the IdP clears its own SSO cookies instead of signing you
back in without asking.
Multiple providers
You can register any number of IdentityProvider resources. The
token-exchange endpoint accepts every provider's tokens, so a second provider
is the normal way to admit CI and workload identities alongside your human
IdP.
Hub enforces uniqueness rules across providers, including one browser-login
holder, no shared issuer URLs, and no overlapping userInfoPrefix values, and
moving browser login between providers takes a specific sequence. See
Multiple providers.
Sample identity providers
The fields above mean the same thing for every provider. What differs is how
each one emits group claims and what its issuer URL looks like, so a complete
worked IdentityProvider (app registration, issuer URL format, group-claim
setup, and directory configuration where it applies) lives on its own page:
A provider that isn't listed still works as long as it's OIDC-compliant: a
stable issuer URL publishing .well-known/openid-configuration, the
authorization code flow, and a claim carrying group membership. The
Keycloak and Okta pages are the closest templates to
start from.
Related resources
- Identity overview: what Hub identifies, and the token lifecycle these settings govern.
- Multiple providers: uniqueness rules and moving browser login between providers.
- Verifying your identity: read back the username and groups your claim mappings produce.
- Directory sync: let Hub search the IdP for subjects to put in role bindings.
- Installing Hub: the Helm values that generate a provider for you.