Skip to main content

api-authz

Kibana API route authorization patterns. Use when configuring route security, working with requiredPrivileges or extendedPrivileges, using authzResult for privilege-based branching, opting out of authorization, or naming custom privileges.

Datos de origen

Repositorio
elastic/kibana
Última actividad en el origen
30 de julio de 2026 a las 13:21
Idioma detectado de SKILL.md
inglés
Estrellas
21.236
Forks
8623

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
api-authz
description
Kibana API route authorization patterns. Use when configuring route security, working with requiredPrivileges or extendedPrivileges, using authzResult for privilege-based branching, opting out of authorization, or naming custom privileges.
# API Authorization > All API routes in Kibana must have authorization checks. Authorization is not optional, even for `internal` routes. ## Route Security Configuration Routes declare authorization via the `security` option in `KibanaRouteOptions`: ```ts router.get({ path: '/api/path', security: { authz: { requiredPrivileges: ['<privilege_1>', '<privilege_2>'], // Optional: checked and surfaced in request.authzResult, but not enforced // extendedPrivileges: ['<optional_privilege>'], }, }, ... }, handler); ``` ## Privilege Naming Privilege names follow the `<operation>_<subject>` convention using underscores only. | Incorrect | Why | Correct | |-----------|-----|---------| | `read-entity-a` | Uses `-` instead of `_` | `read_entity_a` | | `delete_entity-a` | Mixes `_` and `-` | `delete_entity_a` | | `entity_manage` | Subject before operation | `manage_entity` | ## Privilege-Based Branching with `authzResult` When a route handler branches logic based on user privileges (returns different data, enables different features), it **must** use `request.authzResult`. Do not use `capabilities.resolveCapabilities()` or other authorization checks for branching — `authzResult` is the single source of truth. **Look for:** routes that conditionally expose data based on permissions, or functions that check capabilities and return booleans for branching. ### Optional privileges that extend behavior (`extendedPrivileges`) When a privilege **gates access** and another privilege only **extends** what the route returns or allows, declare the gate in `requiredPrivileges` and the optional checks in `extendedPrivileges`. Extended privileges are checked and surfaced in `authzResult` but never produce a 403. `extendedPrivileges` is a **flat list of privilege name strings** only. Privilege sets (`anyRequired` / `allRequired`) are not supported there. When authz is enabled, `requiredPrivileges` is always required by the schema — optional privileges alone cannot protect a route. **Correct — required gate + optional extension:** ```ts router.get({ path: '/api/path', security: { authz: { requiredPrivileges: ['read_entity'], extendedPrivileges: ['read_entity_details'], }, }, ... }, (context, request, response) => { const includeDetails = request.authzResult?.read_entity_details === true; return response.ok({ body: getEntity({ includeDetails }) }); }); ``` **Wrong — abusing `anyRequired` then re-enforcing in the handler:** ```ts router.get({ path: '/api/path', security: { authz: { // Under-declares the real requirement; OAS implies either privilege alone grants access requiredPrivileges: [{ anyRequired: ['read_entity', 'read_entity_details'] }], }, }, }, (context, request, response) => { if (request.authzResult?.read_entity !== true) { return response.forbidden(); // easy to forget → under-protected route } const includeDetails = request.authzResult?.read_entity_details === true; return response.ok({ body: getEntity({ includeDetails }) }); }); ``` ### Mutually exclusive privilege branches (`anyRequired`) When either of several privileges grants access and the handler picks a branch, use `anyRequired` and branch on `authzResult`: ```ts router.get({ path: '/api/path', security: { authz: { requiredPrivileges: ['privilege_3', { anyRequired: ['privilege_1', 'privilege_2'] }], }, }, ... }, (context, request, response) => { const authzResult = request.authzResult; // { "privilege_3": true, "privilege_1": true, "privilege_2": false } if (authzResult.privilege_1) { return response.ok({ body: ... }); } else if (authzResult.privilege_2) { return response.ok({ body: ... }); } return response.ok({ body: { data: ... } }); }); ``` **Wrong — using capabilities for authorization branching:** ```ts const canReadDecryptedParams = async (routeContext: RouteContext) => { const { request, server } = routeContext; const capabilities = await server.coreStart.capabilities.resolveCapabilities(request, { capabilityPath: 'my_capability.*', }); return capabilities.my_capability?.canReadParams ?? false; }; if (await canReadDecryptedParams(routeContext)) { return getDecryptedParams(routeContext, paramId); } else { return getBasicParams(routeContext, paramId); } ``` **Fix:** declare both privileges in the route config with `anyRequired` and branch on `request.authzResult`: ```ts router.get({ path: '/api/params', security: { authz: { requiredPrivileges: [{ anyRequired: ['read_params_decrypted', 'read_params'] }], }, }, }, (context, request, response) => { if (request.authzResult.read_params_decrypted) { return getDecryptedParams(routeContext, paramId); } else { return getBasicParams(routeContext, paramId); } }); ``` ## Opting Out of Authorization When a route must opt out, use the predefined `AuthzOptOutReason` enum or `AuthzDisabled` helpers from `@kbn/core-security-server`: ```ts import { AuthzDisabled, AuthzOptOutReason } from '@kbn/core-security-server'; // Predefined helper router.get({ path: '/api/path', security: { authz: AuthzDisabled.delegateToSOClient }, ... }, handler); // Predefined enum router.get({ path: '/api/path', security: { authz: { enabled: false, reason: AuthzOptOutReason.DelegateToSOClient }, }, ... }, handler); // Custom reason — only when no predefined reason applies router.get({ path: '/api/health', security: { authz: { enabled: false, reason: 'This route is a health check endpoint that returns no sensitive information', }, }, ... }, handler); ``` **Invalid opt-out reasons — flag these:** - `"Opt out from authorization"` — too generic, no context - `"This route does not need authorization"` — no explanation why - `"Authorization not required"` — no context provided - `"Authorization is delegated to SO Client"` — use `AuthzOptOutReason.DelegateToSOClient` instead ## References - [Kibana API Authorization Documentation](dev_docs/key_concepts/api_authorization.mdx) - [Kibana HTTP API Design Guidelines](dev_docs/contributing/kibana_http_api_design_guidelines.mdx)
Ver en GitHub