diff --git a/docs/auth/authentication/index.mdx b/docs/auth/authentication/index.mdx index 4c7c7ffdcb..c06ea07218 100644 --- a/docs/auth/authentication/index.mdx +++ b/docs/auth/authentication/index.mdx @@ -26,7 +26,8 @@ Once your IdP is connected, see [Using Authentication](/documentation/access-con |--------|-----------------|----------| | **Device flow** (browser) | `nemo auth login` | Interactive use — opens browser to sign in with your IdP | | **Password grant** | `nemo auth login --username --password ` | CI/CD pipelines — non-interactive | -| **Direct from IdP** | Use your IdP's token endpoint or workload identity | Custom integrations, service accounts | +| **Scoped Access Key** | `nemo auth access-keys create` | Non-SDK automation, including NeMo service accounts | +| **Direct from IdP** | Use your IdP's token endpoint or workload identity | Custom integrations that require IdP-issued scopes | The CLI stores the token and auto-refreshes it before expiry. The SDK reads the stored token from the CLI config automatically — after `nemo auth login`, `NeMoPlatform()` works with no arguments. diff --git a/docs/auth/authentication/using-authentication.mdx b/docs/auth/authentication/using-authentication.mdx index 1a4efcc822..803eefe005 100644 --- a/docs/auth/authentication/using-authentication.mdx +++ b/docs/auth/authentication/using-authentication.mdx @@ -63,7 +63,7 @@ For CI pipelines, use the password grant to obtain a token without a browser: `n -Password grant sends credentials directly to the IdP and **bypasses MFA**. Many production IdPs disable it. Use a dedicated service account with minimal scopes where possible. +Password grant sends credentials directly to the IdP and **bypasses MFA**. Many production IdPs disable it. Use a dedicated IdP automation identity with minimal scopes where possible. ## Make API Calls @@ -107,8 +107,8 @@ curl -H "Authorization: Bearer $TOKEN" \ ## Scoped Access Keys for Non-SDK Clients When Scoped Access Keys are enabled by the platform administrator, an -authenticated user can mint a scoped bearer token for automation that cannot use -the SDK's OIDC refresh flow: +authenticated user can mint a lifecycle-managed bearer token for automation that +cannot use the SDK's OIDC refresh flow: ```bash # Create a Scoped Access Key with the platform default expiry and print the token once. @@ -119,6 +119,31 @@ Scoped Access Key management commands live under the `auth` namespace as `nemo auth access-keys ...`. The `access-keys` command group is not a top-level CLI command and is not a separate NeMo Platform plugin. +By default, `create` makes a user-bound key for the signed-in user's effective +principal. A human PlatformAdmin can instead create a service-account-bound key: + +```bash +nemo auth access-keys create \ + --service-account intake-otel-writer \ + --name intake-otel-write \ + --expires-in 604800 +``` + +The service account does not need to exist in the IdP and does not run +`nemo auth login`. NeMo stamps the token subject as +`service-account:intake-otel-writer`; grant workspace access to that prefixed +principal with `nemo workspaces members ...`. + + + +The current access-key create CLI supports `--service-account` and +`--expires-in`, but it does not expose per-key API scope selection. For +service-account access keys, use workspace role bindings and short lifetimes to +limit access. API-scope checks still apply to bearer tokens that contain +`scope` or `scp` claims, such as OIDC access tokens. + + + `create` prints the Scoped Access Key token once. Store it in your secret manager and send it in the standard `Authorization` header: @@ -127,8 +152,10 @@ curl -H "Authorization: Bearer $NMP_SCOPED_ACCESS_KEY" \ https://nmp.company.com/apis/entities/v2/workspaces ``` -Scoped Access Keys are signed JWT bearer tokens scoped to the principal and -groups present when the key is created. By default, new keys use the platform's +Scoped Access Keys are signed JWT bearer tokens scoped to a principal identity. +User-bound keys preserve the user's principal and groups at creation time. +Service-account-bound keys use a non-human `service-account:` principal and +do not carry user email or group claims. By default, new keys use the platform's configured default expiry, which is 30 days unless the administrator changes it. Pass `--expires-in ` to request a specific finite lifetime. Pass `--expires-in none` only for deployments where the administrator has explicitly @@ -144,13 +171,16 @@ nemo auth access-keys unsuspend ak_0123456789abcdef0123456789abcdef nemo auth access-keys revoke ak_0123456789abcdef0123456789abcdef ``` -The list includes each key's `ACTIVE`, `EXPIRED`, `SUSPENDED`, or `REVOKED` status -plus its description, issuer, audiences, creation time, and expiration time. Suspension -and revocation take effect on subsequent authenticated platform requests. Use suspension -to temporarily block a key, such as while investigating suspected misuse, without -permanently revoking it. An unexpired suspended key can be restored with `unsuspend`. If -the key expires while suspended, `unsuspend` is a no-op and reports `EXPIRED`. A revoked -key cannot be restored. Rotation is not implemented. +The list includes each key's `ACTIVE`, `EXPIRED`, `SUSPENDED`, or `REVOKED` +status plus its entity type, principal, description, issuer, audiences, creation +time, and expiration time. Regular users see their own user-bound keys. +PlatformAdmins also see all service-account-bound keys, including keys created +by other admins. Suspension and revocation take effect on subsequent +authenticated platform requests. Use suspension to temporarily block a key, such +as while investigating suspected misuse, without permanently revoking it. An +unexpired suspended key can be restored with `unsuspend`. If the key expires +while suspended, `unsuspend` is a no-op and reports `EXPIRED`. A revoked key +cannot be restored. Rotation is not implemented. ### Token Inspection @@ -166,7 +196,7 @@ Decode the token to inspect claims: nemo auth token --decode ``` -Key claims to check: +Key claims to check on OIDC tokens: - `email` or `upn` — the principal identity - `scp` or `scope` — granted scopes @@ -174,6 +204,10 @@ Key claims to check: - `iss` — issuer URL (must match your config) - `aud` — audience (must match your config) +For Scoped Access Keys, check `sub`, `jti`, `nmp_token_type`, and +`nmp_access_key`. Service-account keys have `sub: service-account:` and +`nmp_access_key.entity_type: SERVICE_ACCOUNT`. + ## Token Management ### How Auto-Refresh Works @@ -226,5 +260,6 @@ The OIDC token endpoint is **not** stored — it is discovered at runtime from y - [OIDC](/documentation/access-control/authentication/oidc-setup) — Configure your identity provider. - [API Scopes](/documentation/access-control/authorization/api-scopes) — Scope model and available scopes. +- [Scoped Intake Tokens](/documentation/access-control/deployment/scoped-intake-tokens) — Create service-account access keys for Intake. - [Security Model](/documentation/access-control/security-model) — Trust boundaries and the principal model. - [Troubleshooting](/documentation/access-control/troubleshooting) — Fix common 401/403 errors and login failures. diff --git a/docs/auth/authorization/api-scopes.mdx b/docs/auth/authorization/api-scopes.mdx index 87d28fc67d..82c12d1998 100644 --- a/docs/auth/authorization/api-scopes.mdx +++ b/docs/auth/authorization/api-scopes.mdx @@ -5,20 +5,29 @@ title: "API Scopes" description: "" --- -API scopes are token-level access restrictions that sit on top of role-based permissions. They control which parts of the API a token can access, independent of the user's role. +API scopes are token-level access restrictions that sit on top of role-based +permissions. When a token carries platform API scopes, those scopes control +which parts of the API the token can access, independent of the token +principal's role. For role-based permissions, see [Roles & Permissions](/documentation/access-control/authorization/roles-and-permissions). For the RBAC model, see [Authorization Concepts](/documentation/access-control/concepts). ## How Scopes Work -Scopes are included in OIDC tokens and follow the format `resource-group:access-type`. Each API endpoint is associated with one or more required scopes. +Scopes are read from bearer-token `scope` or `scp` claims and follow the format +`resource-group:access-type`. OIDC access tokens commonly carry these claims. +Scoped Access Key validation also honors scope claims when they are present, but +the current access-key create CLI/API does not expose per-key API scope +selection. Each API endpoint is associated with one or more required scopes. -When a request arrives, the PDP checks: +When a request arrives with platform API scopes, the PDP checks: 1. Does the token have at least one of the endpoint's required scopes? 2. Does the principal have the required role permissions in the workspace? -Both must pass. This is the **two-layer authorization** model. +Both must pass when platform API scopes are present. Tokens with no scope claim, +or only standard OIDC scopes, skip the scope check for compatibility and rely on +the role-permission check. ## Available Scopes @@ -35,6 +44,7 @@ Each API has a read and write scope. A token with an API-specific scope can only | Files | `files:read` | `files:write` | `/apis/files/` | | Guardrails | `guardrails:read` | `guardrails:write` | `/apis/guardrails/` | | Inference | `inference:read` | `inference:write` | `/apis/inference-gateway/` | +| Intake | `intake:read` | `intake:write` | `/apis/intake/` | | Jobs | `jobs:read` | `jobs:write` | `/apis/jobs/` | | Models | `models:read` | `models:write` | `/apis/models/` | | Safe Synthesizer | `safe-synthesizer:read` | `safe-synthesizer:write` | `/apis/safe-synthesizer/` | @@ -54,7 +64,9 @@ The `platform:*` scopes act as catch-alls that grant access to **all** APIs: | `platform:read` | Read access to all platform APIs | | `platform:write` | Write access to all platform APIs | -Each endpoint in the authorization policy lists both its API-specific scope and the corresponding `platform:*` scope. A token needs at least one of the listed scopes to pass the scope check. +Each endpoint in the authorization policy lists both its API-specific scope and +the corresponding `platform:*` scope. When a token provides platform API scopes, +it needs at least one of the listed scopes to pass the scope check. ### Requesting Scopes @@ -73,7 +85,8 @@ nemo auth login --scope "files:read files:write models:read models:write" ## Two-Layer Authorization -For a request to succeed, it must satisfy **both** requirements: +When a token provides platform API scopes, a request must satisfy **both** +requirements: 1. **Token scope check**: The API token must have at least one of the endpoint's required scopes 2. **Permission check**: The principal must have the required permissions through role grants in the workspace @@ -87,11 +100,15 @@ For a request to succeed, it must satisfy **both** requirements: | Viewer | `platform:read platform:write` | Create model | ✗ Denied by role check | | Viewer | `platform:read` | List models | ✓ Allowed | -This enables least-privilege tokens: an Editor can create a read-only token (`platform:read`) for monitoring scripts that should never modify resources. +This enables least-privilege OIDC tokens: an Editor can sign in with +`platform:read` for monitoring scripts that should never modify resources. ## IdP Configuration -Scopes must be registered in your IdP as custom API scopes. The CLI requests them during the OAuth flow, and the IdP includes granted scopes in the access token. +For OIDC tokens, scopes must be registered in your IdP as custom API scopes. The +CLI requests them during the OAuth flow, and the IdP includes granted scopes in +the access token. For NeMo service-account access keys, see +[Scoped Intake Tokens](/documentation/access-control/deployment/scoped-intake-tokens). If your IdP prefixes scopes (e.g., Azure AD uses `api://client-id/platform:read`), configure `scope_prefix` in the NeMo Platform OIDC settings so the platform strips the prefix before authorization: @@ -114,11 +131,14 @@ The PDP distinguishes between OIDC standard scopes and platform scopes: ## When to Restrict Scopes -- **CI/CD tokens** that should only read: `platform:read` -- **Monitoring scripts** that should never modify resources: `platform:read` -- **Data ingestion scripts**: `files:read files:write` +- **OIDC CI/CD tokens** that should only read: `platform:read` +- **OIDC monitoring tokens** that should never modify resources: `platform:read` +- **OIDC data ingestion tokens**: `intake:write` +- **Service-account Intake keys**: grant the `service-account:` principal the + minimum workspace role, such as Viewer for readers or Editor for OTLP writers - **Model catalog readers**: `models:read` -- **Shared service accounts**: limit blast radius by restricting to only the areas needed +- **Shared IdP automation identities**: limit blast radius by requesting only the + scopes needed by that integration ## Related diff --git a/docs/auth/authorization/index.mdx b/docs/auth/authorization/index.mdx index 698e856b74..3de293f8e4 100644 --- a/docs/auth/authorization/index.mdx +++ b/docs/auth/authorization/index.mdx @@ -5,20 +5,24 @@ title: "Authorization" description: "" --- -NeMo Platform authorization controls what authenticated users can do. Every API request is evaluated against the user's token scopes and role bindings before it is allowed. +NeMo Platform authorization controls what authenticated principals can do. Every +API request is evaluated against the principal's role bindings, and against +token scopes when the token carries platform API scopes. The authorization model has four building blocks: 1. **Workspaces** — the authorization boundary. All resources belong to a workspace. 2. **Roles** — permission bundles (Viewer, Editor, Admin) granted per workspace. -3. **Role bindings** — the link between a user, a role, and a workspace. -4. **Scopes** — token-level restrictions that limit what the token can do, independent of the user's role. +3. **Role bindings** — the link between a principal, a role, and a workspace. +4. **Scopes** — token-level restrictions that limit what the token can do, independent of the principal's role. ```text Request → PDP → Scope check → Role binding check → Allow / Deny ``` -For a request to succeed, both the scope check (does the token allow it?) and the role check (does the user have permission?) must pass. +For a request to succeed, the role check must pass. If the token contains +platform API scopes such as `intake:write` or `platform:read`, the scope check +must pass too. For the full conceptual background, see [Authorization Concepts](/documentation/access-control/concepts). For the security architecture, see [Security Model](/documentation/access-control/security-model). diff --git a/docs/auth/authorization/managing-access.mdx b/docs/auth/authorization/managing-access.mdx index 716c7687e7..92dbe73a28 100644 --- a/docs/auth/authorization/managing-access.mdx +++ b/docs/auth/authorization/managing-access.mdx @@ -47,7 +47,10 @@ workspace = client.workspaces.create( ## Managing Workspace Members -Members are users who have been granted access to a workspace. Each member has one of three roles: +Members are principals that have been granted access to a workspace. A principal +can be a human user, an IdP group, the wildcard principal `*`, or a NeMo service +account such as `service-account:intake-otel-writer`. Each member has one of +three roles: - **Viewer** — Read-only access to all resources - **Editor** — Can create, modify, and delete resources @@ -62,7 +65,9 @@ When you add or change a member, the CLI and SDK wait for the change to propagat ### Add a Member -Grant someone access to a workspace by adding them as a member with a specific role. The principal is typically an email address that identifies the user in your identity provider. +Grant a principal access to a workspace by adding it as a member with a specific +role. Human principals are often email addresses from your identity provider, +but the principal value does not have to be an email address. @@ -110,6 +115,53 @@ client.workspaces.members.create( +### Add a Service Account + +Service-account Scoped Access Keys authenticate as `service-account:`. +Create the role binding for that full principal value, not for the unprefixed +service account ID: + + + + + +```bash +nemo workspaces members create \ + --principal service-account:intake-reader \ + --roles Viewer \ + --workspace ml-team +``` + + + + +```python +from nemo_platform import NeMoPlatform + +client = NeMoPlatform() + +client.workspaces.members.create( + workspace="ml-team", + principal="service-account:intake-reader", + roles=["Viewer"], +) +``` + + + + + +Use the unprefixed ID only when creating the key with +`nemo auth access-keys create --service-account intake-reader`. NeMo stamps the +token subject as `service-account:intake-reader`. + + + +Do not grant customer automation access with the `service:` principal +form. The `service:` prefix is reserved for internal platform service +principals. + + ### List Members View all members of a workspace to audit access or verify permissions. The response includes each member's principal, roles, and when access was granted. diff --git a/docs/auth/authorization/roles-and-permissions.mdx b/docs/auth/authorization/roles-and-permissions.mdx index 06806ca411..a663c18712 100644 --- a/docs/auth/authorization/roles-and-permissions.mdx +++ b/docs/auth/authorization/roles-and-permissions.mdx @@ -11,6 +11,15 @@ The authoritative reference for NeMo Platform roles and their permissions. For b NeMo Platform provides human-facing roles for interactive users and a separate workload role for job runtime identities: + + +The same workspace roles can be granted to NeMo service-account principals such +as `service-account:intake-reader`. Service accounts are not internal +`service:` principals; grant them the minimum role needed for their +automation task. + + + **Viewer** — For stakeholders who need visibility into resources but should not modify them. - View all resources in a workspace (models, datasets, jobs, evaluations) diff --git a/docs/auth/concepts.mdx b/docs/auth/concepts.mdx index 72fecc4a59..5896d1738e 100644 --- a/docs/auth/concepts.mdx +++ b/docs/auth/concepts.mdx @@ -128,7 +128,7 @@ The PlatformAdmin role is granted via the `admin_email` config setting. See [Aut A role binding contains: -- **Principal**: The user being granted access (e.g., `alice@company.com`) +- **Principal**: The identity being granted access, such as a user, group, wildcard `*`, or service account - **Workspace**: The workspace where access is granted (e.g., `team-ml`) - **Role**: The role being assigned (e.g., `Editor`) @@ -172,7 +172,9 @@ If a user tries to access a workspace they don't have permission for, the API re ## Policy Decision Point (PDP) -Every authorized request is evaluated by the PDP, which checks role bindings and scopes. The PDP runs in one of two modes: +Every authorized request is evaluated by the PDP, which checks role bindings and, +when the token carries platform API scopes, scopes. The PDP runs in one of two +modes: - **Embedded** (default): A WASM-based policy engine built into the auth service. No external dependencies. - **External OPA**: An Open Policy Agent instance (sidecar or standalone service) fetches policy bundles from the auth service. diff --git a/docs/auth/deployment/configuration.mdx b/docs/auth/deployment/configuration.mdx index 83d89a75bf..8c8bbb9e7d 100644 --- a/docs/auth/deployment/configuration.mdx +++ b/docs/auth/deployment/configuration.mdx @@ -90,10 +90,26 @@ Nested auth keys use a double underscore after `NMP_AUTH_`: for example, ## 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, persists their lifecycle metadata, and rejects service principals. -Users can list and revoke their own keys. Rotation is not implemented. +Scoped Access Keys let an authenticated user create a lifecycle-managed bearer +token for non-SDK clients and automation. The implementation creates signed JWT +access keys, persists their lifecycle metadata, and rejects the reserved +internal `service:` principal prefix. + +By default, a key is bound to the signed-in user's effective principal. A human +PlatformAdmin can create a service-account-bound key by passing +`service_account_id` to the API or `--service-account ` to the CLI. The +issued token authenticates as `service-account:`. Service-account IDs are +NeMo Platform identities, not Kubernetes service account tokens, and they do not +need matching IdP users or email addresses. + +Service account IDs must start with a letter or number and may contain letters, +numbers, `.`, `_`, `+`, `/`, or `-`. Pass the unprefixed ID to +`--service-account`; use the prefixed `service-account:` principal when +granting workspace roles. + +Users can list, suspend, unsuspend, and revoke their own user-bound keys. +PlatformAdmins can also list, suspend, unsuspend, and revoke service-account +keys, including keys created by other admins. Rotation is manual. 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. @@ -135,6 +151,11 @@ 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`. +The current access-key create API does not expose per-key API scope selection. +For service-account access keys, constrain access with workspace role bindings, +short finite lifetimes, suspension, and revocation. API-scope checks still apply +to bearer tokens that contain `scope` or `scp` claims. + 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 @@ -165,6 +186,10 @@ The issuer defaults to `/apis/auth` when when a gateway validates Scoped Access Keys before forwarding requests to platform services. +For a CLI runbook that combines Scoped Access Key enablement checks, Intake +workspace access, and OTLP verification, see +[Scoped Intake Tokens](/documentation/access-control/deployment/scoped-intake-tokens). + ## OIDC Workload Identity Exchange `auth.oidc` can also advertise SDK workload identity token exchange metadata. diff --git a/docs/auth/deployment/gateway.mdx b/docs/auth/deployment/gateway.mdx index 29a149ae52..a67bb4e092 100644 --- a/docs/auth/deployment/gateway.mdx +++ b/docs/auth/deployment/gateway.mdx @@ -37,6 +37,7 @@ For IdP-issued tokens, enforce your IdP's revocation policy before setting NeMo **Security Requirement**: Your ingress/gateway **must** strip the following headers from all incoming external requests before forwarding to NeMo Platform: - `X-NMP-Principal-Id`, `X-NMP-Principal-Email`, `X-NMP-Principal-Groups`, `X-NMP-Principal-On-Behalf-Of` +- `X-NMP-Principal-On-Behalf-Of-Email`, `X-NMP-Principal-On-Behalf-Of-Groups` - `X-NMP-Scopes` If external clients can set these headers, they can forge any identity or bypass authorization entirely. The gateway should also block external access to `/internal/*` paths (used for service-to-service communication). @@ -49,19 +50,20 @@ When the gateway has authenticated the bearer token, it must forward: | Header | Description | |--------|-------------| -| `X-NMP-Principal-Id` | Principal identifier (e.g., user ID or email). Required. | -| `X-NMP-Principal-Email` | User email (optional but recommended). | -| `X-NMP-Principal-Groups` | Comma-separated group names (optional). | -| `X-NMP-Scopes` | Space-separated token scopes (optional). | +| `X-NMP-Principal-Id` | Principal identifier, such as a user ID, email, or `service-account:`. Required. | +| `X-NMP-Principal-Email` | User email, when present in the authenticated token. | +| `X-NMP-Principal-Groups` | Comma-separated group names, when present in the authenticated token. | +| `X-NMP-Scopes` | Space-separated token scopes, when the authenticated token has `scope` or `scp` claims. | Header names are case-insensitive; services normalize them. ## Bypass Paths -The following are **not** subject to authorization checks; they are always allowed: +The following are **not** sent through the gateway bearer-auth callout: - **Health and readiness**: `/health`, `/healthz`, `/ready`, `/readyz`, `/health/live`, `/health/ready`, `/metrics` - **Discovery**: `/apis/auth/discovery` (for CLI/SDK OIDC discovery) +- **Signing keys**: `/apis/auth/jwks` (for NeMo-minted bearer-token signing keys) - **PDP endpoints**: Paths under `/apis/auth/v2/authz/` are restricted to service principals only. The middleware rejects external and regular-user requests automatically. - **Studio**: Paths under `/studio` (the Studio UI handles its own OIDC login) @@ -123,6 +125,8 @@ route_config: - "x-nmp-principal-email" - "x-nmp-principal-groups" - "x-nmp-principal-on-behalf-of" + - "x-nmp-principal-on-behalf-of-email" + - "x-nmp-principal-on-behalf-of-groups" - "x-nmp-scopes" routes: - match: { prefix: "/" } diff --git a/docs/auth/deployment/hardening.mdx b/docs/auth/deployment/hardening.mdx index 1a00a84288..732cf9bc0c 100644 --- a/docs/auth/deployment/hardening.mdx +++ b/docs/auth/deployment/hardening.mdx @@ -28,7 +28,7 @@ For the security architecture, see [Security Model](/documentation/access-contro ## Gateway and Network -- [ ] **Strip auth headers from external requests**: Configure your ingress/gateway to remove `X-NMP-Principal-Id`, `X-NMP-Principal-Email`, `X-NMP-Principal-Groups`, `X-NMP-Principal-On-Behalf-Of`, and `X-NMP-Scopes` from all incoming external traffic. See [Gateway Integration](/documentation/access-control/deployment/gateway-integration). +- [ ] **Strip auth headers from external requests**: Configure your ingress/gateway to remove `X-NMP-Principal-Id`, `X-NMP-Principal-Email`, `X-NMP-Principal-Groups`, `X-NMP-Principal-On-Behalf-Of`, `X-NMP-Principal-On-Behalf-Of-Email`, `X-NMP-Principal-On-Behalf-Of-Groups`, and `X-NMP-Scopes` from all incoming external traffic. See [Gateway Integration](/documentation/access-control/deployment/gateway-integration). - [ ] **Enable TLS termination**: Terminate TLS at the ingress or load balancer. Tokens in `Authorization` headers are sent in the clear without TLS. - [ ] **Consider gateway bearer-auth callout**: To enforce bearer-token validation before requests reach platform services, configure Envoy `ext_authz` to call `/apis/auth/ext-authz`. See [Gateway Integration](/documentation/access-control/deployment/gateway-integration). diff --git a/docs/auth/deployment/scoped-intake-tokens.mdx b/docs/auth/deployment/scoped-intake-tokens.mdx new file mode 100644 index 0000000000..7cf59291fe --- /dev/null +++ b/docs/auth/deployment/scoped-intake-tokens.mdx @@ -0,0 +1,375 @@ +--- +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +title: "Scoped Intake Tokens" +description: "Create service-account Scoped Access Keys for reading Intake data and sending OTLP telemetry." +--- +Use this runbook when you already have a NeMo Platform deployment with +authentication enabled and need bearer tokens for non-interactive Intake clients. +Typical clients include OTLP exporters, collectors, jobs, and automation scripts. + +The CLI calls these credentials **Scoped Access Keys**. They are NeMo-minted JWT +bearer tokens with a configurable lifetime and lifecycle state. They are not +Kubernetes service account tokens, and this flow does not use workload identity +token exchange. + +In this flow, a platform admin signs in as a human admin user and creates keys +for stable NeMo service accounts. The service account does not need an IdP user, +email address, password, refresh token, or interactive login flow. + + + +In this release, `nemo auth access-keys create` supports user-bound keys and +service-account-bound keys, but it does not expose per-key API scope selection. +Service-account keys created by this CLI are constrained by the service-account +principal, workspace role bindings, expiration, suspension, and revocation. API +scope checks still apply to bearer tokens that contain `scope` or `scp` claims, +such as OIDC access tokens requested with `nemo auth login --scope ...`. + + + +## Access Model + +Scoped Access Keys are constrained by: + +- the principal ID stamped into the token +- the principal's workspace role bindings +- the key expiration time +- the key suspension or revocation state + +For service-account keys, the admin passes a service account ID to +`--service-account`. NeMo stamps the token subject as +`service-account:`. + +Use separate service accounts for read and write access: + +| Token | `--service-account` value | Principal to grant | Minimum role | Intended use | +|---|---|---|---|---| +| Intake reader token | `intake-reader` | `service-account:intake-reader` | Viewer | Fetch spans, traces, sessions, evaluator results, or annotations | +| Intake OTLP writer token | `intake-otel-writer` | `service-account:intake-otel-writer` | Editor | Send OTLP, ATIF, chat-completions, or direct span data | + +The built-in Viewer and Editor roles are workspace-wide. A reader service +account with Viewer can read other Viewer-accessible resources in the workspace. +A writer service account with Editor can write other Editor-accessible resources +in the workspace. If you need an Intake-only API scope stamped into the +service-account key itself, that is not exposed by the current access-key create +CLI/API. + + + +Do not use the `service:` prefix for customer automation identities. NeMo +Platform reserves `service:` for internal platform service principals with +different authorization behavior. Customer service-account access keys use the +`service-account:` principal form. + + + +## Before You Start + +Set these variables for the existing deployment and target workspace: + +```bash +export NMP_BASE_URL=https://nmp.company.com +export WORKSPACE=team-workspace + +export INTAKE_READER_SERVICE_ACCOUNT_ID=intake-reader +export INTAKE_WRITER_SERVICE_ACCOUNT_ID=intake-otel-writer +export INTAKE_READER_PRINCIPAL="service-account:${INTAKE_READER_SERVICE_ACCOUNT_ID}" +export INTAKE_WRITER_PRINCIPAL="service-account:${INTAKE_WRITER_SERVICE_ACCOUNT_ID}" + +export READ_KEY_TTL_SECONDS=604800 +export WRITE_KEY_TTL_SECONDS=604800 +``` + +Service account IDs must start with a letter or number and may contain letters, +numbers, `.`, `_`, `+`, `/`, or `-`. Pass the unprefixed ID to +`--service-account`. Use the prefixed `service-account:` value when granting +workspace access. + +This runbook assumes: + +- NeMo Platform auth is already enabled. +- Your IdP, login flow, and gateway auth path already work. +- A human platform admin can sign in with the CLI. +- The admin has the `PlatformAdmin` role in the `system` workspace. +- A platform or workspace admin can manage workspace members for the target + workspace. +- Scoped Access Keys are enabled, or a platform operator can enable them through + the documented deployment configuration. + +Set `NMP_BASE_URL` to the final HTTPS origin. Do not send authenticated requests +through HTTP redirects. + +## Confirm Existing Auth + +Verify the public URL and sign in as the human admin who will create the service +tokens: + +```bash +curl -fsS "$NMP_BASE_URL/apis/auth/discovery" | jq '.auth_enabled' +nemo auth login --base-url "$NMP_BASE_URL" +nemo auth status +nemo workspaces members list --workspace "$WORKSPACE" +``` + +The discovery command should print `true`. The admin signs in with the normal +user flow. The service accounts created later do not run `nemo auth login`. + +Confirm the CLI supports service-account Scoped Access Keys: + +```bash +nemo auth access-keys create --help +``` + +The help output should include `--service-account` and `--expires-in`. If those +options are missing, upgrade to a NeMo Platform release that supports +service-bound Scoped Access Keys. + +If your deployment uses Entra, no Entra-specific changes are required just to +create NeMo service-account access keys. NeMo Platform uses the admin's +authenticated session to authorize key creation, then stamps +`service-account:` into the service token. + +## Enable Scoped Access Keys + +If `nemo auth access-keys list` already succeeds, skip this section. + +Scoped Access Keys must be enabled by a platform operator before admins can +create service tokens. This runbook intentionally does not repeat deployment or +Helm upgrade commands. Use your normal release process and the +[Scoped Access Keys configuration](/documentation/access-control/deployment#scoped-access-keys) +reference. + +For chart-based deployments, the relevant settings are under +`platformConfig.auth`: + +- `token_signing.issuer` +- `token_signing.key_id` +- `token_signing.private_key_file` +- `access_keys.enabled` +- `access_keys.issue_format` +- `access_keys.accepted_formats` +- `access_keys.audience` +- `access_keys.default_expires_in_seconds` +- `access_keys.max_expires_in_seconds` + +The operator must also provide a stable RSA signing key at the configured +`token_signing.private_key_file` path. Keep this key backed up. Rotating it +invalidates existing keys. + +The lifetime controls are: + +- `auth.access_keys.default_expires_in_seconds`: default lifetime when a create + request omits `--expires-in`. +- `auth.access_keys.max_expires_in_seconds`: maximum finite lifetime that admins + may request. +- `nemo auth access-keys create --expires-in `: lifetime for one key, + capped by `max_expires_in_seconds`. +- `nemo auth access-keys create --expires-in none`: request a key with no + expiration. This only works when `auth.access_keys.max_expires_in_seconds` is + `null`. + +After the operator applies the configuration, verify access-key support: + +```bash +curl -fsS "$NMP_BASE_URL/apis/auth/discovery" | jq '.auth_enabled' +curl -fsS "$NMP_BASE_URL/apis/auth/jwks" | jq '.keys[].kid' +nemo auth access-keys list +``` + +The discovery command should print `true`. The JWKS output should include the +configured access-key signing `kid`. + +If `nemo auth access-keys list` returns `Scoped Access Keys are not enabled`, +ask the operator to complete the access-key configuration and redeploy through +the documented platform deployment process. + +Gateway requirements are covered in +[Gateway Integration](/documentation/access-control/deployment/gateway-integration). + +## Grant Intake Access + +Grant read access to the reader service account: + +```bash +nemo workspaces members create \ + --workspace "$WORKSPACE" \ + --principal "$INTAKE_READER_PRINCIPAL" \ + --roles Viewer +``` + +Grant write access to the OTLP writer service account: + +```bash +nemo workspaces members create \ + --workspace "$WORKSPACE" \ + --principal "$INTAKE_WRITER_PRINCIPAL" \ + --roles Editor +``` + +Verify the grants: + +```bash +nemo workspaces members list --workspace "$WORKSPACE" +``` + +## Create And Verify A Reader Token + +Create the token from the admin CLI session: + +```bash +export NMP_INTAKE_READ_TOKEN="$( + nemo auth access-keys create \ + --service-account "$INTAKE_READER_SERVICE_ACCOUNT_ID" \ + --name intake-read \ + --description "Read Intake data from $WORKSPACE" \ + --expires-in "$READ_KEY_TTL_SECONDS" +)" +``` + +The CLI prints the token only once. Store the token in your secret manager. + +Verify the token resolves as a Scoped Access Key: + +```bash +curl -fsS "$NMP_BASE_URL/apis/auth/authenticate" \ + -H "Authorization: Bearer $NMP_INTAKE_READ_TOKEN" \ + | jq '{principal, token_kind, jti, scopes}' +``` + +Expected fields: + +```json +{ + "principal": "service-account:intake-reader", + "token_kind": "access_key", + "jti": "ak_...", + "scopes": [] +} +``` + +An empty `scopes` list is expected for CLI-created access keys in this release. +Authorization is enforced by the service-account principal's workspace role. + +Use the reader token to fetch Intake data: + +```bash +curl -g --fail-with-body \ + -H "Authorization: Bearer $NMP_INTAKE_READ_TOKEN" \ + "$NMP_BASE_URL/apis/intake/v2/workspaces/$WORKSPACE/spans?page=1&page_size=1" +``` + +Confirm the reader token cannot write: + +```bash +curl -sS -o /tmp/read-token-write-check.json -w "%{http_code}\n" \ + -X POST \ + -H "Authorization: Bearer $NMP_INTAKE_READ_TOKEN" \ + -H "Content-Type: application/x-protobuf" \ + --data-binary @/dev/null \ + "$NMP_BASE_URL/apis/intake/v2/workspaces/$WORKSPACE/ingest/otlp/v1/traces" +``` + +The expected status is `403`. If it succeeds, check whether the reader service +account was granted Editor, Admin, wildcard, or PlatformAdmin access. + +## Create And Verify An OTLP Writer Token + +Create the writer token from the admin CLI session: + +```bash +export NMP_INTAKE_WRITE_TOKEN="$( + nemo auth access-keys create \ + --service-account "$INTAKE_WRITER_SERVICE_ACCOUNT_ID" \ + --name intake-otel-write \ + --description "Write OTLP telemetry to $WORKSPACE" \ + --expires-in "$WRITE_KEY_TTL_SECONDS" +)" +``` + +Verify the token resolves as a Scoped Access Key: + +```bash +curl -fsS "$NMP_BASE_URL/apis/auth/authenticate" \ + -H "Authorization: Bearer $NMP_INTAKE_WRITE_TOKEN" \ + | jq '{principal, token_kind, jti, scopes}' +``` + +Expected `principal` is `service-account:intake-otel-writer`, `token_kind` is +`access_key`, `jti` starts with `ak_`, and `scopes` is empty. + +Verify the OTLP write path accepts the token: + +```bash +curl -sS -o /tmp/write-token-otlp-check.json -w "%{http_code}\n" \ + -X POST \ + -H "Authorization: Bearer $NMP_INTAKE_WRITE_TOKEN" \ + -H "Content-Type: application/x-protobuf" \ + --data-binary @/dev/null \ + "$NMP_BASE_URL/apis/intake/v2/workspaces/$WORKSPACE/ingest/otlp/v1/traces" +cat /tmp/write-token-otlp-check.json +``` + +The expected status is `200`, with `{"errors":[]}`. This empty OTLP request +verifies authorization and endpoint wiring. Send a real trace from your agent or +collector, then query it back with the reader token. + +Configure an OTLP/HTTP exporter with the writer token: + +```bash +export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="$NMP_BASE_URL/apis/intake/v2/workspaces/$WORKSPACE/ingest/otlp/v1/traces" +export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf +export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20$NMP_INTAKE_WRITE_TOKEN" +``` + +## Operate And Rotate Keys + +`nemo auth access-keys list` lists keys owned by the signed-in user. A current +PlatformAdmin also sees all service-account-bound keys, including keys created +by other admins. + +List keys: + +```bash +nemo auth access-keys list +nemo auth access-keys list --page 2 --page-size 100 +``` + +Suspend, restore, or revoke a key by its `jti`: + +```bash +nemo auth access-keys suspend ak_0123456789abcdef0123456789abcdef +nemo auth access-keys unsuspend ak_0123456789abcdef0123456789abcdef +nemo auth access-keys revoke ak_0123456789abcdef0123456789abcdef +``` + +Service-account keys are platform-owned. Any current human PlatformAdmin can +list, suspend, unsuspend, or revoke them by `jti`. A service-account key cannot +create or manage other service-account keys, even if someone grants that service +account PlatformAdmin. + +Rotation is not automatic. Create a replacement key, update the external secret +or collector configuration, verify the replacement, then revoke the old key. + +## Troubleshooting + +| Symptom | Likely cause | Check | +|---|---|---| +| `Scoped Access Keys are not enabled` | `auth.access_keys.enabled` is false in the running config, or the signing key is not configured | Ask the operator to check the access-key configuration and `curl "$NMP_BASE_URL/apis/auth/jwks"` | +| `nemo auth access-keys create --help` has no `--service-account` | The CLI or platform release does not support service-bound Scoped Access Keys | Upgrade the CLI and platform together | +| `403` while creating a service-account key | The signed-in caller is not a current human PlatformAdmin, or auth is disabled | Confirm the caller has `PlatformAdmin` in the `system` workspace | +| `/apis/auth/authenticate` returns `"scopes": []` | Expected for CLI-created access keys in this release | Use workspace role bindings to constrain access | +| Need a service token with an `intake:write` claim | The current access-key create CLI/API does not expose per-key API scopes | Use an IdP-issued token with the required scope for API-scope-only controls | +| `401` from `/apis/auth/authenticate` | Bad, expired, suspended, revoked, or wrong-audience key | Run `nemo auth access-keys list` as a PlatformAdmin | +| Reader token can write | Reader service account has Editor, Admin, wildcard, or PlatformAdmin access | Check `nemo workspaces members list --workspace "$WORKSPACE"` and system role bindings | +| `403` from a read request | Reader service account is not Viewer or higher in the workspace | Check the workspace member list | +| `403` from OTLP ingest | Writer service account is not Editor or higher in the workspace | Check the workspace member list | +| OTLP exporter works, but no spans appear | Empty test batch or missing semantic telemetry | Send a real OpenInference or OTel GenAI trace and query by `session_id` | + +## Related + +- [Auth Configuration](/documentation/access-control/deployment) +- [Managing Access](/documentation/access-control/authorization/managing-access) +- [Gateway Integration](/documentation/access-control/deployment/gateway-integration) +- [Using Authentication](/documentation/access-control/authentication/using-authentication) diff --git a/docs/auth/security-model.mdx b/docs/auth/security-model.mdx index b59dfb805f..634fbb46d3 100644 --- a/docs/auth/security-model.mdx +++ b/docs/auth/security-model.mdx @@ -26,7 +26,7 @@ sequenceDiagram IdP-->>Client: JWT Client->>Gateway: Authorization: Bearer token Gateway->>Auth: /apis/auth/authenticate - Auth-->>Gateway: Trusted X-NMP-Principal-* and X-NMP-Scopes headers + Auth-->>Gateway: Trusted X-NMP-Principal-* headers and optional X-NMP-Scopes Gateway->>Service: Forward request with trusted headers Service->>PDP: Check authorization PDP-->>Service: Allow / deny @@ -36,7 +36,8 @@ The request flow: 1. **Client** sends a request with a bearer token in the `Authorization` header. 2. In gateway deployments, **Gateway** calls the auth service's `/apis/auth/authenticate` endpoint; otherwise, the first service validates the token directly. -3. The validating component derives trusted `X-NMP-Principal-*` and `X-NMP-Scopes` headers. +3. The validating component derives trusted `X-NMP-Principal-*` headers and + `X-NMP-Scopes` when the token carries scope claims. 4. **PDP** (Policy Decision Point) evaluates authorization — checks the principal's role bindings and token scopes against the operation's requirements. 5. If allowed, the service handles the request. @@ -49,7 +50,12 @@ In quickstart deployments without an OIDC provider, the `X-NMP-Principal-*` head NeMo Platform supports two authentication modes: **service-level** bearer-token validation and **gateway bearer-auth callout**. -In both modes, identity arrives in the `Authorization: Bearer ` header. The bearer token is validated exactly once — either by the first NeMo Platform service or by the auth service behind the gateway callout. After validation, the authenticated identity (email, subject, groups) is propagated to downstream services via **trusted `X-NMP-Principal-*` headers**. Downstream services accept these headers without re-validating the token, but they still run authorization checks. +In both modes, identity arrives in the `Authorization: Bearer ` header. +The bearer token is validated exactly once, either by the first NeMo Platform +service or by the auth service behind the gateway callout. After validation, the +authenticated identity is propagated to downstream services via **trusted +`X-NMP-Principal-*` headers**. Downstream services accept these headers without +re-validating the token, but they still run authorization checks. This "validate once, propagate via headers" design means that **network perimeter security is critical**: anything inside the trust boundary that receives `X-NMP-Principal-*` headers will trust them unconditionally. The gateway must strip these headers from all incoming external requests to prevent clients from forging an identity. See [Gateway Integration](/documentation/access-control/deployment/gateway-integration). @@ -70,7 +76,7 @@ No gateway is required. This is the simplest mode and the default. The gateway (e.g., Envoy with `ext_authz`) authenticates the bearer token before forwarding the request: 1. Gateway calls `/apis/auth/authenticate` with the original `Authorization` header -2. Auth service validates the bearer token and returns trusted `X-NMP-Principal-*` and `X-NMP-Scopes` headers +2. Auth service validates the bearer token and returns trusted `X-NMP-Principal-*` headers, plus `X-NMP-Scopes` when the token carries scope claims 3. Gateway forwards the request with those trusted headers 4. Services skip bearer-token re-validation and run their normal PDP authorization checks @@ -78,10 +84,17 @@ This rejects unauthenticated requests before they reach platform services while ## Principal Model -A **principal** is an authenticated identity — typically a human user identified by email address. Each request has exactly one principal. +A **principal** is an authenticated identity. It can be a human user, a group +identifier used in role bindings, a NeMo service account, or an internal +platform service principal. Each request has exactly one principal. When OIDC is enabled, the principal is resolved from JWT claims: the `sub` claim becomes the principal ID (or `oid` for Azure AD), the `email` claim provides the email (or `upn` for Azure AD), and group memberships come from the `groups` claim. These claim names are configurable. In gateway deployments, the auth service performs this extraction and the gateway forwards the returned `X-NMP-Principal-*` headers. +Scoped Access Keys can also authenticate as the signed-in user's principal, or +as a NeMo service account when a PlatformAdmin creates the key with +`--service-account `. Service-account keys use the principal ID +`service-account:` and do not require an IdP user or email address. + **Quickstart shortcut** — When running without OIDC (`email-as-API-key` mode), the principal is the raw value of the `X-NMP-Principal-Id` header, without any token validation. This is intended for quick testing only. @@ -93,14 +106,37 @@ The following headers carry the authenticated identity through the system: | Header | Description | |---|---| -| `X-NMP-Principal-Id` | Unique identifier for the principal (required). Resolved from the JWT `sub` claim (or `oid` for Azure AD). | -| `X-NMP-Principal-Email` | The principal's email address. | -| `X-NMP-Principal-Groups` | Comma-separated list of groups the principal belongs to (from JWT group claims). | -| `X-NMP-Scopes` | Space-separated list of token scopes (extracted from the JWT `scp` or `scope` claim). Used by the PDP for scope-based authorization checks. | +| `X-NMP-Principal-Id` | Unique identifier for the principal (required). For OIDC, this is resolved from the JWT `sub` claim or the configured subject claim. For service-account access keys, this is `service-account:`. | +| `X-NMP-Principal-Email` | The principal's email address, when the authenticated token has one. | +| `X-NMP-Principal-Groups` | Comma-separated list of groups the principal belongs to, when the authenticated token has group claims. | +| `X-NMP-Scopes` | Space-separated list of token scopes, when the authenticated token has a `scp` or `scope` claim. Used by the PDP for scope-based authorization checks. | These headers are set after bearer-token validation and forwarded on every internal service-to-service call. As described in [Authentication Modes](#authentication-modes), services inside the trust boundary accept them unconditionally — they are never re-validated. Services still call the PDP for authorization when `auth.enabled=true`. -### Service Principals +### Service Accounts + +Service accounts are customer-facing non-human identities for automation. A +PlatformAdmin can create a service-account Scoped Access Key with: + +```bash +nemo auth access-keys create --service-account intake-otel-writer +``` + +The issued token authenticates as `service-account:intake-otel-writer`. +Service-account principals are not privileged by default. Grant them workspace +roles the same way you grant roles to users: + +```bash +nemo workspaces members create \ + --principal service-account:intake-otel-writer \ + --roles Editor \ + --workspace ml-team +``` + +Service accounts do not run `nemo auth login`, do not require email addresses, +and are not Kubernetes service account tokens. + +### Internal Service Principals Not all requests originate from human users. Platform services that need cross-workspace access — for example, the jobs controller monitoring jobs across all users, or the evaluator coordinating evaluations — authenticate as **service principals**. @@ -120,20 +156,19 @@ Some operations require a service to act with its own credentials while preservi - **Secret access**: A user cannot fetch a secret value directly — only the platform can — but the platform still needs to verify that the user has permission to access the secret's workspace. - **Entity store access**: Feature-specific APIs (Models, Evaluation, etc.) check user permissions at the service level, then access the entity store using service credentials with the user's identity attached for `created_by`/`updated_by` attribution. -In these cases, the calling service sets the `X-NMP-Principal-On-Behalf-Of` header to a JSON object containing the original user's identity: - -```json -{ - "id": "user-principal-id", - "email": "alice@example.com", - "groups": [ - "team-ml", - "data-eng" - ] -} +In these cases, the calling service sets its own principal plus separate +on-behalf-of headers for the original user's identity: + +```http +X-NMP-Principal-Id: service:evaluator +X-NMP-Principal-On-Behalf-Of: user-principal-id +X-NMP-Principal-On-Behalf-Of-Email: alice@example.com +X-NMP-Principal-On-Behalf-Of-Groups: team-ml,data-eng ``` -The downstream service constructs a principal from this object and evaluates the *user's* permissions — including group-based access — while accepting the request from the service identity. +The downstream service constructs a delegated principal from those headers and +evaluates the *user's* permissions, including group-based access, while +accepting the request from the service identity. #### Job Credential Propagation @@ -152,12 +187,18 @@ NeMo Platform uses **workspace-scoped Role-Based Access Control (RBAC)**. All re - The wildcard principal `*` binds a role for all authenticated users at once - Workspaces are private by default; the creator becomes Admin automatically -On top of RBAC, NeMo Platform supports **API scopes** as a second authorization layer at the token level. Every authorized request passes through two independent checks: +On top of RBAC, NeMo Platform supports **API scopes** as a second authorization +layer at the token level. When a bearer token has platform API scopes, an +authorized request must pass two independent checks: 1. **Scope check** (token level): The JWT must carry at least one of the scopes required by the endpoint (e.g., `platform:read` or `platform:write`). Scopes limit what the *token* can do. 2. **Permission check** (role level): The principal must have the necessary permissions via role bindings in the workspace. Roles limit what the *user* can do. -Both must pass. This enables least-privilege token usage — for example, an Editor can create a read-only token (`platform:read` only) for monitoring scripts. +Both must pass when platform API scopes are present. Tokens with no scope claim, +or only standard OIDC scopes such as `openid`, `profile`, and `email`, skip the +scope check for compatibility and rely on the permission check. This enables +least-privilege OIDC token usage, for example an Editor signing in with +`platform:read` only for monitoring scripts. For details, see [Authorization Concepts](/documentation/access-control/concepts), [Roles & Permissions](/documentation/access-control/authorization/roles-and-permissions), and [API Scopes](/documentation/access-control/authorization/api-scopes). diff --git a/docs/auth/troubleshooting.mdx b/docs/auth/troubleshooting.mdx index 8c0cf2d098..b05f3593b4 100644 --- a/docs/auth/troubleshooting.mdx +++ b/docs/auth/troubleshooting.mdx @@ -130,7 +130,7 @@ The `client_id` in NeMo Platform config doesn't match the application in your Id 1. Check that the gateway calls `/apis/auth/authenticate` with the original `Authorization` header before forwarding protected requests. -2. Check that the gateway forwards the trusted `X-NMP-Principal-*` and `X-NMP-Scopes` headers returned by the auth service on successful authentication. +2. Check that the gateway forwards the trusted `X-NMP-Principal-*` headers returned by the auth service on successful authentication, plus `X-NMP-Scopes` when the token carries scope claims. 3. Check that auth headers are stripped from external requests. Try sending a request with `X-NMP-Principal-Id` or `X-NMP-Scopes` from outside the cluster — those headers should be stripped by the gateway. diff --git a/docs/fern/versions/latest.yml b/docs/fern/versions/latest.yml index 7425e6f9e1..57b8b34e93 100644 --- a/docs/fern/versions/latest.yml +++ b/docs/fern/versions/latest.yml @@ -476,6 +476,8 @@ navigation: path: ../../auth/deployment/credential-propagation.mdx - page: Gateway Integration path: ../../auth/deployment/gateway.mdx + - page: Scoped Intake Tokens + path: ../../auth/deployment/scoped-intake-tokens.mdx - page: Production Hardening path: ../../auth/deployment/hardening.mdx - page: Security Model