Skip to content

Authentication and organisation roles

Authentication establishes who called SASY; roles determine which APIs that caller may use. The policy then decides whether a particular action is permitted. Keep these three decisions distinct.

For normal SDK use, provision a separate, independently generated random API key for each principal. Treat the complete value as opaque: clients send it unchanged, and the server maps it to an entity. Entity names, roles and tenants belong in the server configuration; they are not secrets embedded in a key.

The existing API-key provider accepts literal keys when SASY_API_KEY_SUFFIX is unset. Its JSON schema is:

{
"type": "api_key",
"metadata_key": "x-api-key",
"static_keys": {
"<GENERATE_A_UNIQUE_RANDOM_KEY_FOR_WORKER>": "worker",
"<GENERATE_A_DIFFERENT_RANDOM_KEY_FOR_REVIEWER>": "reviewer"
}
}

These placeholders are not credentials; replace each with an independent secret before loading the file with --auth-provider. Map worker and reviewer to their required roles and tenants in your --auth-config file. Store the provider configuration privately and distribute only the relevant full key to each client.

Python sends the full key through an explicit hook:

import os
import sasy
from sasy.auth.hooks import APIKeyAuthHook
sasy.configure(
sasy_url=os.environ["SASY_URL"],
auth_hook=APIKeyAuthHook(api_key=os.environ["SASY_API_KEY"]),
)

APIKeyAuthHook sends the full value verbatim. The TypeScript equivalent is new ApiKeyAuthHook(fullKey). Python also reads SASY_API_KEY from the environment or .env when no explicit hook is supplied. An explicit hook wins. Neither SDK reads a key suffix, derives a credential from an entity name, or selects a demo principal.

When migrating older SDK code, replace entity_api_key, entityApiKey, ApiKeyAuthHook.forEntity, and entity-key constructor options with a complete key provisioned for that principal. Remove the old suffix-only client configuration and set SASY_API_KEY instead. Missing client credentials do not fall back to a public demo key.

The engine still accepts the legacy server-side SASY_API_KEY_SUFFIX setting for existing demo deployments. Unset it on a server configured with literal keys: a nonempty value appends to every configured key. Existing demo provisioning must deliver the complete resulting key to the client; the SDK does not compose it. This shared-suffix scheme cannot separate principals: a holder of one key with a known public prefix can recover the suffix and construct other configured keys. Prefer independent keys, and rotate the entire old key family when moving across trust boundaries. The engine continues to refuse shipped bare key names by default.

SASY’s credential-injection sources are separate from these client authentication keys. SQLite is suitable for local development; deployments can use environment-seeded memory or a read-only OpenBao source. Injection fails closed when the selected credential source errors. The application owns credential storage and rotation.

For OIDC, copy config/auth/jwt.example.json to the ignored local config/auth/jwt.json, edit it with the provider’s exact JWKS URL, issuer and SASY audience, then start the engine with that file as --auth-provider and your --auth-config mapping. Obtain those URLs from your provider’s discovery document; the example domain is intentionally not usable. The JWT provider verifies signatures and configured issuer/audience. It reads the bearer token from authorization, resolves the entity from sub by default, and caches JWKS keys for the configured interval.

SASY does not automatically translate an organisation’s groups into its roles. Choose one approach:

  • With use_central_roles: true, map stable authenticated entity values in config/auth_config.yaml to SASY roles and tenants. A missing entity has no configured roles. Replace the local example entities with real subjects before enabling OIDC.
  • With use_central_roles: false, configure roles_claim to a claim issued by your trusted identity provider. Nested names are supported. That claim must already contain SASY role names; configure the provider’s group-to-role mapping yourself. Clients cannot assign themselves privileged roles.
Role Intended capability
observability-writer Register graph observations.
observability-reader Read observations within permitted scope.
reference-monitor-user Request action checks and bind permitted session policies.
admin Administrative policy operations and privileged exceptions.
service-proxy Trusted relay delegation; reserve for infrastructure.

Tenant-wide policy changes and privileged bypasses require the relevant authenticated and delegated identities to satisfy the server’s checks. Do not give every client admin or service-proxy to make a demo work. Serve remote clients over TLS and provision trust roots independently. The local example uses API keys over server-authenticated TLS; mTLS is a separate deployment choice.