Skip to content

Give a machine its own identity

Every other tutorial signs you in as a person: plexctl login opens a browser, you authenticate, and you get a session. A deploy pipeline or a background worker has no browser and no person to click the button. It still needs an identity. This lesson gives one to a machine.

You will authenticate a ServiceIdentity with the OAuth2 client_credentials grant — the machine presents a client id and secret instead of a human logging in — and receive a token. Then, acting as that machine, you will list, rotate, and revoke that token. Along the way you will hit a deliberate wall: a machine cannot mint fresh tokens, only rotate the one it was granted. By the end you will understand how plexsphere models machine identity as the deliberate counterpart to human identity.

This lesson takes about fifteen minutes.

Before you start

This lesson is standalone: it does not build on the explore, build, or Group lessons. You only need a running stack from Set up your local plexsphere, plus jq on your $PATH. Recreate the shell environment:

bash
export PATH="$PWD/bin:$PATH"
export PLEXSPHERE_URL=http://localhost:8080

The demo stack seeds one machine identity for you to use. It lives in a dedicated svc-demo tenant — the machine tenant, with no human sign-in — and its credentials are fixture values, never real secrets:

bash
CLIENT_ID=svc-deployer
CLIENT_SECRET=svc-deployer-demo-secret

Why does the demo ship a separate identity provider for this? The human login flow runs against Dex, which does not implement the client_credentials grant at all. The seed therefore runs a small second provider just for machine credentials, bound to the svc-demo tenant. You never talk to it directly — plexsphere does, on your behalf.

Step 1 — Authenticate the machine

A human runs plexctl login. A machine runs issue-service: it presents its client id and secret, plexsphere verifies them with the tenant's identity provider, and hands back a Bearer token bound to the ServiceIdentity. Capture that token:

bash
TOKEN=$(plexctl identity-tokens issue-service \
  --client-id "$CLIENT_ID" \
  --grant-type client_credentials \
  --client-secret "$CLIENT_SECRET" \
  --output json | jq -r '.access_token')

printf '%s' "$TOKEN" > /tmp/svc.token
test -s /tmp/svc.token && echo "machine token captured"
text
machine token captured

That token is the machine's credential, exactly as a session is yours. Everything you run next with --token-file /tmp/svc.token acts as the machine, not as you.

Step 2 — Confirm who the machine is

Ask the platform to resolve the token. whoami works for a machine principal just as it does for a person:

bash
plexctl whoami --token-file /tmp/svc.token
text
PRINCIPAL  SUBJECT                               DOMAIN                                ACR  AMR
service    <service-identity-uuid>               <svc-demo-uuid>                       -    -

The principal is service, not a user — the platform knows this is a machine. There is no ACR or AMR, because the machine did not pass through a human authentication context; it presented a secret. Capture the machine's own id for later:

bash
SVC_ID=$(plexctl whoami --token-file /tmp/svc.token --output json \
  | jq -r '.subject')

Step 3 — See the token the machine holds

A machine principal can see the tokens it owns. List them:

bash
plexctl identity-tokens list --token-file /tmp/svc.token
text
ID                                    ENV   CREATED_AT            EXPIRES_AT            LAST_USED_AT  ROTATED_AT
<api-token-uuid>                      live  <timestamp>           <timestamp>           -             -

Capture the id of the token the machine is currently authenticated with — you will rotate it in a moment:

bash
TOKEN_ID=$(plexctl identity-tokens list --token-file /tmp/svc.token \
  --output json | jq -r '.items[0].id')

Step 4 — A machine cannot mint a fresh token

Here is the wall. Try to have the machine mint a brand-new token for itself, the way a person mints a personal API token:

bash
plexctl identity-tokens issue \
  --identity-ref "service:$SVC_ID" \
  --env-prefix dev \
  --token-file /tmp/svc.token
text
plexctl: Forbidden: a caller authenticated via an API token cannot mint another API token; sign in interactively to issue tokens

Forbidden, with a non-zero exit. This is deliberate. A caller that authenticated with an API token cannot use it to mint more API tokens — that would let one leaked token spawn an unbounded family of credentials. Minting fresh tokens is an interactive, human-only act. A machine's credential lifecycle is not minting; it is rotation, which is the next step.

Step 5 — Rotate the machine's token

A machine can rotate the token it already holds. Rotate the one from Step 3:

bash
plexctl identity-tokens rotate \
  --id "$TOKEN_ID" \
  --token-file /tmp/svc.token
text
# WARNING: this is the only time this plaintext will be displayed
psk_live_<new-plaintext>

id:                 <new-api-token-uuid>
env_prefix:         live
expires_at:         <timestamp>
rotation_deadline:  <timestamp>

Rotation is not a hard cut-over. Until rotation_deadline — a 48-hour overlap window — both the old and the new plaintext are honoured, so a fleet of workloads can pick up the new secret on their own schedule without a coordinated restart. After the deadline, only the new plaintext works. This overlap is what makes unattended rotation safe, and it is the day-to-day operation a machine identity actually performs.

Capture the new plaintext from the line below the banner. Nothing on the server returns it again — in a real pipeline you would hand it to the workload immediately.

Step 6 — Revoke and clean up

When a machine is decommissioned, its tokens are revoked. Delete the token and remove the captured credential from your machine:

bash
plexctl identity-tokens delete \
  --id "$TOKEN_ID" \
  --token-file /tmp/svc.token

rm -f /tmp/svc.token

delete returns no output and exits 0 on success; the next request that presents the token is rejected. The ServiceIdentity itself is untouched — it can authenticate again with issue-service whenever it needs a fresh token, just as a person can log in again after logging out.

What you learned

  • A machine is a first-class identity. A ServiceIdentity authenticates with client_credentials and gets a token, exactly as a person authenticates with a browser and gets a session.
  • issue-service is the machine's login. It is the non-interactive counterpart to plexctl login: no browser, no human, just a client id and secret verified against the tenant's identity provider.
  • Minting is interactive; rotation is for machines. A caller holding an API token cannot mint new tokens — that anti-escalation guard is deliberate. A machine rotates the token it was granted instead.
  • Rotation overlaps, it does not cut over. Until the rotation deadline both plaintexts work, so a fleet rotates on its own schedule.
  • The machine tenant is deliberately separate. Machine credentials run against their own identity provider because the human login flow cannot serve them — identity is modelled, not improvised.

Where to go next