Trust, with clear boundaries.
Know who holds each secret, what a token permits, and how to change keys without breaking valid requests.
Orionis 0.6.01. Separate responsibilities
Each participant holds only the material it needs to do its job. JWTs carry a service identity and permissions; the receiving API independently validates them.
| Participant | Holds | Uses it to |
|---|---|---|
| Calling service | Its client ID, plaintext client secret, and cached access tokens. | Authenticate to Orionis and call allowed APIs. |
| Orionis auth server | Client secret digests, grants, and private signing keys. | Authenticate callers and sign access tokens. |
| Resource service | Expected issuer/audience and public verification keys. | Verify tokens and enforce route permissions. |
A resource API needs its own client credentials only if it also
calls another service. Register billing-api as an
audience for receiving requests; give billing a separate caller
identity when it makes outbound calls.
2. Grant precise access
Give each calling service its own client ID and a narrow set of audiences and concrete scopes. The receiving API checks both the target and the required action.
{
"id": "orders-service",
"secret_sha256_hex": ["<SHA256_HEX_OF_CLIENT_SECRET>"],
"allowed_audiences": ["billing-api"],
"allowed_scopes": [
"billing.invoice.create",
"billing.invoice.read"
],
"default_scopes": ["billing.invoice.read"]
}
secret_sha256_hex is an array of lowercase SHA-256
hex digests. The caller still authenticates with the original
secret, so store that value in its secret manager. Never use a
digest as the caller’s password.
Default scopes are used when the token request omits scope. For
creating invoices, explicitly request
billing.invoice.create. Keep the server’s allowed
scopes and the API’s route requirements aligned.
3. Bound token lifetime
The default access-token TTL is 15 minutes; choose a short lifetime appropriate to your service. A token is accepted only while its signature, issuer, audience, time claims, token use, and route scopes are valid.
- Use a token only for its intended audience.
- Keep service clocks synchronized and choose clock leeway deliberately.
- Keep the verification algorithm policy and issuer explicit.
- Treat scopes as service permissions. User authorization belongs in the application’s own policy.
The Go client renews cached tokens when a request needs a replacement. The remote JWKS provider separately caches public keys. Review the key-cache lifecycle when choosing refresh and stale windows for your availability needs.
4. Rotate signing keys
Use keys to publish overlapping public keys and
active_kid to select the signer for new tokens. Keep
the issuer stable through the rotation.
05 / Overlapping signing-key rotation
-
T0 / CURRENT STATE
Sign with the old key.
Consumers already know its public key.
old · active -
T1 / PUBLISH BOTH
Add the new key to JWKS.
Deploy both key references and replace tasks while the old key stays active. Verify both public keys are available.
old · activenew · published -
T2 / SWITCH THE SIGNER
Set active_kid to the new key.
Roll out the change. Verify new tokens use the new kid and consumers accept tokens signed by either key.
old · verificationnew · active -
T3 / RETIRE THE OLD KEY
Wait, then remove.
After the last old signer stops, wait the maximum old-token lifetime plus accepted clock skew. Then remove the old key and roll out again.
new · active
Inject production Ed25519 private keys as PKCS8 PEM values through
private_key_pem_env. Keep them readable only by the
auth server and manage backups through your normal
secret-management process.
active_kid: signing-new
keys:
- kid: signing-old
private_key_pem_env: ORIONIS_SIGNING_KEY_PEM_OLD
- kid: signing-new
private_key_pem_env: ORIONIS_SIGNING_KEY_PEM
5. Operate the boundary
Run shared environments behind TLS. Restrict access to private keys and caller secrets, keep logs free of credentials, and use readiness together with an authorized consumer request to check a rollout.
| Control | How to use it |
|---|---|
| TLS | Protect token requests and Bearer API traffic in shared environments. |
| Rate limits | Keep token and readiness limits enabled; tune for expected traffic and probes. |
| Audit logs | Record authentication outcomes without tokens, secrets, or request bodies. |
| Log level | Use info, warn, or error outside local debugging. |
| Runtime configuration | Validate a complete candidate, deploy AppConfig, then replace Orionis tasks. |
Built-in rate limiting is per process, endpoint, and remote IP. Account for load balancers and multiple tasks when choosing limits; use an ingress-level control if you need a shared global limit.
rate_limits:
token:
enabled: true
limit: 60
window: "1m"
readyz:
enabled: true
limit: 300
window: "1m"
audit_logs:
enabled: true
log_level: info
These are the example limits, not capacity targets. Tune them for your environment. For deployment steps and validation, return to the rollout guide.