diff --git a/packages/documentation/content/docs/router/configuration/persisted_documents.mdx b/packages/documentation/content/docs/router/configuration/persisted_documents.mdx index 43bab073..6650958f 100644 --- a/packages/documentation/content/docs/router/configuration/persisted_documents.mdx +++ b/packages/documentation/content/docs/router/configuration/persisted_documents.mdx @@ -19,8 +19,33 @@ Type is `boolean` and default is `false`. ### `require_id` -If `true`, requests must contain a resolvable persisted document ID. If Router cannot extract an -ID, it returns `PERSISTED_DOCUMENT_ID_REQUIRED`. Type is `boolean` and default is `false`. +Controls whether requests must contain a resolvable persisted document ID. If a request lacks an +ID and `require_id` is enforced, Router returns `PERSISTED_DOCUMENT_ID_REQUIRED`. + +Accepts a static boolean (`true` or `false`, default is `false`) or an **expression** that is +evaluated per-request. The expression can reference: + +| Variable | Description | +| ------------------ | ------------------------------------ | +| `.request.headers` | HTTP headers of the incoming request | +| `.request.method` | HTTP method (`GET`, `POST`, etc.) | +| `.request.url` | Full request URL | +| `env("VAR")` | Environment variable at request time | + +When the expression returns `true`, the document ID requirement is enforced, but when `false` it is +skipped for that request. + +```yaml +persisted_documents: + require_id: + expression: | + is_null(env("BYPASS_SECRET")) + || .request.headers."x-bypass-require-id" != env("BYPASS_SECRET") +``` + +You can also bypass the requirement on a per-request basis at runtime by setting +`"hive::persisted_documents::skip_enforcement": true` in the request context - see +[Persisted Documents security guide](/docs/router/security/persisted-documents#per-request-bypass). ### `log_missing_id` diff --git a/packages/documentation/content/docs/router/customizations/coprocessors/stages-and-protocol.mdx b/packages/documentation/content/docs/router/customizations/coprocessors/stages-and-protocol.mdx index ffc3d100..68507e02 100644 --- a/packages/documentation/content/docs/router/customizations/coprocessors/stages-and-protocol.mdx +++ b/packages/documentation/content/docs/router/customizations/coprocessors/stages-and-protocol.mdx @@ -281,6 +281,12 @@ The `include.context` property supports three forms: | `hive::telemetry::client_name` | ✅ | The name of the client application | | `hive::telemetry::client_version` | ✅ | The version of the client application | +### Persisted Documents + +| Property | Mutable | Description | +| --------------------------------------------- | :-----: | --------------------------------------------------------------------------------- | +| `hive::persisted_documents::skip_enforcement` | ✅ | Skip the `require_id` check for this request. Set to `true` to bypass enforcement | + ## Body - Some stages allow body mutation, but `graphql.analysis` does not. diff --git a/packages/documentation/content/docs/router/security/persisted-documents.mdx b/packages/documentation/content/docs/router/security/persisted-documents.mdx index 92c6f86c..bee32402 100644 --- a/packages/documentation/content/docs/router/security/persisted-documents.mdx +++ b/packages/documentation/content/docs/router/security/persisted-documents.mdx @@ -91,6 +91,35 @@ With `require_id: false` (default), regular GraphQL requests (with `query`) are With `require_id: true`, incoming requests must provide a persisted document ID. During migration, `log_missing_id: true` helps you find requests that still arrive without an ID. +### Conditional enforcement with expressions + +In addition to a static boolean, `require_id` accepts an **expression** evaluated per-request. +The expression can inspect the request headers, method, URL, and environment variables. +When it returns `true`, the ID is required, otherwise it is not. + +```yaml +persisted_documents: + require_id: + expression: | + is_null(env("BYPASS_SECRET")) + || .request.headers."x-bypass-require-id" != env("BYPASS_SECRET") +``` + +### Per-request bypass + +If you need to skip the `require_id` check on individual requests (for example during a gradual +migration or for trusted internal clients), set the `hive::persisted_documents::skip_enforcement` +flag to `true` in the request context. + +The flag is writable from: + +- `OnHttpRequest` plugin hook +- `OnGraphqlParams` plugin hook +- `router.request` coprocessor stage + +When `skip_enforcement` is `true`, requests with a full operation string pass through even when +`persisted_documents.require_id` is enabled. + ## Path selector and GraphQL endpoint compatibility If you use `url_path_param`, do not use root GraphQL endpoint (`http.graphql_endpoint: "/"`).