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.

Informations de source

Dépôt
elastic/kibana
Dernière activité de la source
30 juillet 2026 à 13:21
Langue détectée de SKILL.md
anglais
Étoiles
21 236
Forks
8 623

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
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)
Voir sur GitHub