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.0

1. 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.

Who holds which credentials
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.

JSON · client registration excerpt
{
  "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

  1. T0 / CURRENT STATE

    Sign with the old key.

    Consumers already know its public key.

    old · active
  2. 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
  3. 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
  4. 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
Publishing first and retiring last lets old and new tokens coexist. Count the overlap from the last token the old signer could have issued, including rolling-deployment overlap.

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.

YAML · overlap with the new signer active
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.

Operational controls
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.

YAML · operational controls
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.

Continue the guideReview the integration examples