Configuration Reference

View as Markdown

Complete reference for enabling and configuring platform authorization: the auth section in config, Helm values, environment variables, and the choice between embedded and external OPA.

For quickstart setup, see Authentication and Authorization. For OIDC settings, see OIDC Setup.

Enabling Authorization

Authorization is enabled in the platform config by setting auth.enabled: true. This can be done in the platform config file:

1auth:
2 enabled: true

When using Helm, this is done by setting platformConfig.auth.enabled: true in your Helm values; this becomes auth.enabled in the calculated platform config.

1# values.yaml
2
3platformConfig:
4 auth:
5 enabled: true

When auth.enabled is false (the default), all API requests are allowed without checks. When true, every request is evaluated by the Policy Decision Point (PDP). In Helm deployments, this setting is controlled via platformConfig.auth.enabled.

Bootstrap Admin

When authorization is enabled, a platform administrator can be configured. Setting admin_email tells platform seed which identity should receive the PlatformAdmin role. Use it to create the first workspaces and grant roles to other users. After bootstrap, manage access via workspaces and members as described in Managing Access.

1auth:
2 enabled: true
3 admin_email: "your-admin@company.com"

The binding is created by platform-seed, not by auth service startup alone. Helm deployments run the platform-seed Job by default. For source installs, set platform.seed_on_startup: true, export NMP_SEED_ON_STARTUP=true before nemo services run, or run uv run python -m nmp.platform_seed after services are healthy. If platform seed has not run, admin_email users can authenticate but still receive 403 Forbidden because their PlatformAdmin binding does not exist.

Platform seed also creates the default wildcard workspace bindings: Editor on the default workspace, Viewer on system, and WorkspaceCreator on system. That last binding is what preserves open workspace creation by default. Remove or replace it if you want workspace creation restricted to designated users or groups.

This page covers the auth-specific configuration fields you need to enable and operate authorization. Auth-related values are found under platformConfig.auth in the values file.

For OIDC-specific fields (auth.oidc), see OIDC Setup.

Authorization Engine: Embedded vs External OPA

The PDP can run in two modes. For technical details, see Policy Engine.

Embedded (default)

  • Provider: policy_decision_point_provider: "embedded".
  • The auth service runs a built-in WASM policy engine. No OPA sidecar is required.
  • Policy data (role bindings, scopes, etc.) is loaded from the entity store and refreshed on an interval (policy_data_refresh_interval).

Use embedded for new deployments and when you do not already have an OPA fleet.

External OPA

  • Provider: policy_decision_point_provider: "opa".
  • An external OPA sidecar (or server) fetches policy bundles from the auth service and evaluates requests.
  • Set policy_decision_point_base_url to the OPA service URL (e.g., http://opa:8181).
  • bundle_cache_seconds controls how long OPA caches the bundle.

Use external OPA when you already use OPA for other services or need a single policy engine at the edge.

Environment Variables

Configuration can be overridden with environment variables using the NMP_AUTH_ prefix. Names are derived from the config keys in UPPER_SNAKE_CASE.

Examples:

$NMP_AUTH_ENABLED=true
$NMP_AUTH_POLICY_DECISION_POINT_BASE_URL=http://auth:8000
$NMP_AUTH_POLICY_DECISION_POINT_PROVIDER=embedded
$NMP_AUTH_ADMIN_EMAIL=admin@example.com
$NMP_AUTH_EMBEDDED_PDP_AUTO_BUILD_WASM=true
$NMP_AUTH_TOKEN_SIGNING__PRIVATE_KEY_FILE=/etc/nmp/workload-token/private-key.pem

Nested auth keys use a double underscore after NMP_AUTH_: for example, NMP_AUTH_OIDC__ISSUER, NMP_AUTH_OIDC__CLIENT_ID, and NMP_AUTH_ACCESS_KEYS__ENABLED.

Scoped Access Keys

Scoped Access Keys let an authenticated user create a scoped bearer token for non-SDK clients and automation. The implementation creates user-scoped signed JWT access keys and rejects service principals. Revocation and rotation are not implemented.

Scoped Access Keys are an auth-service feature exposed under the auth CLI namespace (nemo auth access-keys ...) and the /apis/auth/v2/access-keys API routes. They are not a standalone NeMo Platform plugin.

Scoped Access Keys are disabled by default. Enable them only when the auth service has a shared RSA signing key:

1auth:
2 enabled: true
3 token_signing:
4 issuer: "https://nmp.company.com/apis/auth"
5 key_id: "nemo-platform-signing"
6 private_key_file: "/etc/nmp/workload-token/private-key.pem"
7 access_keys:
8 enabled: true
9 issue_format: "jwt"
10 accepted_formats: ["jwt"]
11 audience: "nemo-platform-access-key"
12 default_expires_in_seconds: 2592000
13 max_expires_in_seconds: 2592000

Equivalent environment overrides:

$NMP_AUTH_TOKEN_SIGNING__ISSUER=https://nmp.company.com/apis/auth
$NMP_AUTH_TOKEN_SIGNING__KEY_ID=nemo-platform-signing
$NMP_AUTH_TOKEN_SIGNING__PRIVATE_KEY_FILE=/etc/nmp/workload-token/private-key.pem
$NMP_AUTH_ACCESS_KEYS__ENABLED=true
$NMP_AUTH_ACCESS_KEYS__ISSUE_FORMAT=jwt
$NMP_AUTH_ACCESS_KEYS__ACCEPTED_FORMATS=jwt
$NMP_AUTH_ACCESS_KEYS__AUDIENCE=nemo-platform-access-key
$NMP_AUTH_ACCESS_KEYS__DEFAULT_EXPIRES_IN_SECONDS=2592000
$NMP_AUTH_ACCESS_KEYS__MAX_EXPIRES_IN_SECONDS=2592000

List-valued env vars for accepted Scoped Access Key formats are written as comma-separated values, for example NMP_AUTH_ACCESS_KEYS__ACCEPTED_FORMATS=jwt.

Service-level AuthorizationMiddleware accepts Scoped Access Key bearer tokens directly when auth.access_keys.enabled=true. When auth.access_keys.enabled=false, the middleware does not try to authenticate Scoped Access Keys. Gateway deployments that use /apis/auth/authenticate still keep auth.access_keys.enabled=true when they want Scoped Access Keys to work, because the same flag controls creation and auth-service validation.

Scoped Access Keys expire after 30 days by default. Create requests may omit expires_in_seconds; the auth service then uses auth.access_keys.default_expires_in_seconds. Callers may request a shorter or longer finite lifetime with expires_in_seconds or CLI --expires-in, but the requested value must be less than or equal to auth.access_keys.max_expires_in_seconds when the max is configured.

Set auth.access_keys.max_expires_in_seconds to null only when unlimited keys are permitted. With max_expires_in_seconds: null and a finite default_expires_in_seconds, ordinary omitted-expiry requests still use the default lifetime, while explicit expires_in_seconds: null or CLI --expires-in none creates a no-expiration key. Set both max and default to null only when omitted-expiry requests should also create no-expiration keys. With a finite max_expires_in_seconds and default_expires_in_seconds: null, callers must provide a finite expires_in_seconds. When max_expires_in_seconds is null, callers may still provide a finite expires_in_seconds, and the auth service honors that finite lifetime.

The issuer defaults to <platform.base_url>/apis/auth when auth.token_signing.issuer is unset. Use an externally reachable issuer URL when a gateway validates Scoped Access Keys before forwarding requests to platform services.

OIDC Workload Identity Exchange

auth.oidc can also advertise SDK workload identity token exchange metadata. Workload tokens share auth.token_signing by default, while auth.oidc.workload_token_* fields remain available for workload-specific overrides:

1auth:
2 token_signing:
3 issuer: "https://nmp.company.com/apis/auth"
4 key_id: "nemo-platform-signing"
5 private_key_file: "/etc/nmp/workload-token/private-key.pem"
6 oidc:
7 enabled: true
8 token_endpoint: "https://idp.example.com/oauth/token"
9 workload_token_exchange_enabled: true
10 workload_client_id: "nemo-platform-workload"
11 workload_token_endpoint: "https://idp.example.com/oauth/token"
12 workload_audience: "nemo-platform"
13 workload_scope: "openid email groups"

By default, workload-exchange access tokens use auth.token_signing.key_id as their JWT kid. To give workload tokens a distinct key identifier, set auth.oidc.workload_token_key_id. For cryptographic separation, also set auth.oidc.workload_token_private_key_file; changing only the key ID is useful for debugging and rotation labeling, but still reuses the same RSA private key.

When NMP_WORKLOAD_IDENTITY_TOKEN_FILE is present, the SDK reads the subject token from that file and sends an RFC 8693 exchange request using subject_token, fixed JWT subject/access-token token types, optional audience, and optional scope. workload_token_endpoint is optional; when it is unset, the SDK uses token_endpoint. This is useful when host CLI login and workload containers need different network-reachable IdP URLs.

For Docker-backed job runtimes, the executor may also need a controller-side subject-token issuer. Configure only Docker-specific fields under the Docker executor profile:

1jobs:
2 executors:
3 - provider: cpu
4 profile: workload
5 backend: docker
6 config:
7 workload_identity:
8 token_endpoint: "https://idp.example.com/oauth/token"
9 client_id: "nemo-platform-workload"
10 username: "svc-nemo"
11 password_env_var: "WORKLOAD_IDENTITY_PASSWORD"
12 scope: "openid email groups"

The password value is read from the controller process environment using password_env_var. The config does not support an inline password field.

For Kubernetes-backed job runtimes, the jobs backend can project a Kubernetes service account token into each workload pod for token exchange. The projected token expiration defaults to 600 seconds and must be at least 600 seconds. In platform configuration, set jobs.executors[].config.workload_identity_token_expiration_seconds on the Kubernetes job executor profile. For example, this changes the projected token lifetime from the 600-second default to one hour:

1jobs:
2 executors:
3 - provider: cpu
4 profile: workload
5 backend: kubernetes_job
6 config:
7 workload_identity_token_expiration_seconds: 3600

Example Configurations

Quickstart / development (auth disabled)

1auth:
2 enabled: false

Quickstart / development (auth enabled)

1auth:
2 enabled: true
3 policy_decision_point_provider: embedded
4 policy_decision_point_base_url: "http://localhost:8080"
5 embedded_pdp_auto_build_wasm: true
6 admin_email: "admin@example.com"

When running from a source checkout, embedded PDP startup can automatically build a missing policy.wasm with the pinned OPA version used by make build-policy. Packaged deployments should include policy.wasm at image or wheel build time; set embedded_pdp_auto_build_wasm: false to fail fast if that artifact is missing. After changing policy source files, run make build-policy to refresh the artifact.

In offline development environments, provide the pinned OPA binary explicitly:

$OPA_BIN=/path/to/opa_linux_amd64_static uv run nemo services run --host 127.0.0.1 --port 8080

Or seed the local cache used by script/build_policy_wasm.sh:

$mkdir -p .cache/opa/v1.8.0
$cp /path/to/opa_linux_amd64_static .cache/opa/v1.8.0/opa_linux_amd64_static
$chmod +x .cache/opa/v1.8.0/opa_linux_amd64_static

Production with embedded PDP

1auth:
2 enabled: true
3 policy_decision_point_base_url: "http://auth:8000"
4 policy_decision_point_provider: embedded
5 embedded_pdp_auto_build_wasm: false
6 policy_data_refresh_interval: 30
7 admin_email: "platform-admin@company.com"
8 oidc:
9 enabled: true
10 issuer: "https://login.microsoftonline.com/<tenant>/v2.0"
11 client_id: "<client-id>"

Production with external OPA

1auth:
2 enabled: true
3 policy_decision_point_base_url: "http://opa:8181"
4 policy_decision_point_provider: opa
5 bundle_cache_seconds: 5