Registering clients
Before an application can sign people in through Zeabur Identity, you register it as a client. Each
client is an OAuthClient object in Zeabur Identity's namespace, and its name is the application's
client_id. kubectl is a client too; Get started registered it.
zeabur-identityctl client create builds the object for you: it checks your input, prints the object,
and generates a secret when the client needs one. Pipe its output into kubectl apply. The
commands below assume the cluster from Get started (namespace
zeabur-identity) and run with the cluster admin's kubeconfig.
First decide which kind of client the application is:
| Type | For | Authenticates with |
|---|---|---|
public | kubectl, desktop and browser-only apps: anything that runs on the user's device | its client_id and PKCE |
confidential | web apps with a server side (Grafana, Argo CD, Headlamp, …) | a secret, plus PKCE |
Public clients
Section titled “Public clients”This is the kubectl client from Get started:
zeabur-identityctl client create kubectl --type public \ --redirect-uri http://127.0.0.1:8000/ --audience kubernetes | kubectl apply -f -Running client create for a name that already exists replaces that client, so always pass all
of its options. Leaving out --audience kubernetes here, for example, stops kubectl from
getting tokens that kube-apiserver accepts.
The options:
--redirect-uriis where the browser goes back to after sign-in. Repeat it for several.--audience kuberneteslets the client get tokens that kube-apiserver accepts. Add it only when the application calls the Kubernetes API as the signed-in person.--home-urlis where people open the application, such ashttps://zeabur.example.com/. With it, the sign-in page offers "Open display name" when someone reaches it with nothing to sign in to, and after they set up a passkey. Anyone who can load the sign-in page sees these names and addresses.https://only, written as for redirect URIs.
Redirect URIs
Section titled “Redirect URIs”A redirect URI is one of:
https://with a host name, such ashttps://app.example.com/callback;http://127.0.0.1/…orhttp://[::1]/…, for apps that receive the result on the user's own machine. Any port matches, sokubectlstill works when it has to listen on another port.
Zeabur Identity compares redirect URIs exactly, character by character. To prevent near-misses,
zeabur-identityctl accepts a URI only in the form a browser sends it:
- lowercase scheme and host, and no default port (
:443); - at least a
/path:https://app.example.com/, nothttps://app.example.com; - non-ASCII written as
xn--host names and percent-encoded paths.
It rejects fragments (#…), user names in the URI, plain http to any other host, and
addresses a browser would refuse, such as 999.1.1.1 or port 99999.
Confidential clients
Section titled “Confidential clients”zeabur-identityctl client create grafana --type confidential --display-name Grafana \ --redirect-uri https://grafana.example.com/login/generic_oauth | kubectl apply -f -The command prints the client secret in your terminal (on stderr), separately from the object.
The secret never ends up in the object, in kubectl's input or in Git. It is shown only
once: the cluster keeps just its SHA-256 hash.
Give the secret to the application right away, for example as a Secret in the application's namespace. Paste it in as below rather than passing it as a command argument, which other users on the machine can see in the process list:
read -rs CLIENT_SECRET # paste the secret, then Enter# monitoring: the namespace Grafana runs in.printf %s "$CLIENT_SECRET" | kubectl -n monitoring create secret generic grafana-oidc \ --from-file=client-secret=/dev/stdinunset CLIENT_SECRETThen configure the application with three values: the issuer URL, the client_id (here
grafana) and the secret. It finds everything else through
<issuer>/.well-known/openid-configuration. Zeabur Identity accepts the secret either way applications
send it: HTTP Basic (client_secret_basic) or a form field (client_secret_post).
Never choose a secret yourself
Section titled “Never choose a secret yourself”Always let zeabur-identityctl client create generate the secret. The hash is visible to anyone who can
read OAuthClient objects or your Git repository, and it has no salt. That is safe for a long
random secret, but a password someone chose can be guessed back from its hash offline, and
Zeabur Identity would never notice.
Without zeabur-identityctl, generate 32 random bytes and hash them yourself:
CLIENT_SECRET=$(openssl rand -hex 32)printf 'sha256:%s\n' "$(printf %s "$CLIENT_SECRET" | sha256sum | cut -d' ' -f1)" # secretHashReplace a secret
Section titled “Replace a secret”Run the same zeabur-identityctl client create command again and apply its output. The new hash
replaces the old one, and the old secret stops working right away, so update the application's
Secret in the same step.
Change or remove a client
Section titled “Change or remove a client”Edit the object like any other (kubectl -n zeabur-identity edit oauthclient grafana), or run
zeabur-identityctl client create with the new options and apply it. That also replaces the secret of a
confidential client.
kubectl -n zeabur-identity get oauthclients # short name: zbclientkubectl -n zeabur-identity delete oauthclient grafana # nobody can sign in to it any moreGitOps
Section titled “GitOps”The printed object contains no secret, so you can commit it. Convert it to YAML if you prefer:
zeabur-identityctl client create grafana --type confidential --display-name Grafana \ --redirect-uri https://grafana.example.com/login/generic_oauth \ | kubectl create --dry-run=client -o yaml -f - > grafana-client.yamlEach run generates a new secret, so give the one it prints to the application before you apply
the file. Use kubectl create --dry-run=client, not kubectl apply --dry-run=client: apply
adds a last-applied-configuration annotation, and, if the client already exists, its
resourceVersion, which makes the file fail to apply later.
If you edit the object by hand, the cluster checks it when you apply it: a plain http or
fragment redirect URI, or a malformed secretHash, fails at kubectl apply rather than at
sign-in. The cluster's URL check is looser than zeabur-identityctl's, though, so run new redirect URIs
through zeabur-identityctl client create to catch the rest.