checkId: check.security.authority-signing-key plugin: stellaops.doctor.security severity: fail tags: [security, auth, keys, jwks]

Authority Signing Key (JWKS)

This Doctor check verifies that the Authority service publishes at least one usable, non-expired token-signing key via its JWKS endpoint. If Authority’s signing-key source is unavailable, JWT issuance fails and OpenIddict is disabled, breaking all downstream authentication platform-wide — so this is a platform-wide blast-radius check.

It is a live, out-of-process probe. It replaces the dead, fully-simulated check.auth.signing-key (in the unwired StellaOps.Doctor.Plugin.Auth), whose CheckSigningKeyAsync returned a hardcoded ActiveKeyId = "key-2024-01-15" and always passed — a dangerous false-green. That dead plugin is a separate dead-plugin/wiring cleanup item (DOC-TRUTH-004); this check supersedes the concept with a real probe and does not revive the dead plugin.

What It Checks

Resolves the Authority issuer from the canonical manifest entry integration.identity.authority-signing-key — config keys Authority:Issuer / Authority:Url / StellaOps:AuthorityUrl / Oidc:Issuer (AUTHORITY_ISSUER is the env form). It then:

  1. Fetches the OIDC discovery document (<issuer>/.well-known/openid-configuration) and reads jwks_uri (falling back to well-known JWKS paths /.well-known/jwks.json, /.well-known/jwks, /jwks when discovery omits it).
  2. GETs the JWKS and inspects each key’s public parameters: a key counts as a usable signing key when it has a recognized kty (RSA/EC/OKP/oct), is not marked use=enc, and — if it carries an x5c certificate chain — the leaf certificate is within its validity window.
ConditionResult
Issuer not configuredskip
Discovery has no jwks_uri and no fallback JWKS path respondsfail
JWKS endpoint unreachable / non-2xxfail
JWKS is empty or no published key is usable for signingfail
Every signing-key certificate is expiredfail
At least one usable signing key, some present keys carry expired certswarn
At least one usable, non-expired signing keypass

Air-gap/on-prem safe: it talks only to the configured (local) Authority — no external network, no cloud-managed KMS, no key generation/rotation. A JWKS publishes only public key parameters; private key material is never read or emitted. Certificate chains are inspected for expiry only.

Why It Matters

The Authority signing key signs every JWT the platform issues. If its source is unavailable or empty, no tokens can be issued and authentication stops platform-wide. The only other real signing probe today targets the Attestor — not Authority — so without this check an Authority signing-key outage was invisible to Doctor.

Common Causes

How to Fix

Never auto-generate or auto-rotate Authority signing keys from Doctor. Repair the key source, then restart Authority to converge.

Docker Compose

# Inspect what Authority publishes
curl -fsS https://authority.stella-ops.local/.well-known/openid-configuration
curl -fsS "$(curl -fsS https://authority.stella-ops.local/.well-known/openid-configuration | python -c 'import sys,json;print(json.load(sys.stdin)["jwks_uri"])')"

# Repair the signing key source (on-prem / Vault only — no cloud-managed KMS default),
# then restart Authority to reload it:
docker compose -f devops/compose/docker-compose.stella-ops.yml restart authority
docker compose -f devops/compose/docker-compose.stella-ops.yml logs --tail 100 authority

Bare Metal / systemd

# Confirm Authority:Signing:KeySource / KeyPath / ActiveKeyId resolve to a loadable, non-expired key
# (e.g. /etc/stellaops/authority.appsettings.json), then:
systemctl restart stellaops-authority
journalctl -u stellaops-authority --no-pager -n 100

Kubernetes / Helm

kubectl get pods -n stellaops -l app=stellaops-authority
# Repair the signing-key Secret / Vault reference, then roll Authority:
kubectl rollout restart deploy/stellaops-authority -n stellaops

Verification

stella doctor run --check check.security.authority-signing-key