Skip to main content

API authorization

Kubernetes TokenReview authenticates a caller. Orka then submits a SubjectAccessReview for each permission listed below using that caller's username, UID, groups, and extra attributes. The permission must cover the final namespace, resource, verb, and name. Missing clients, review errors, denied decisions, and ambiguous results return 403 before the requested operation runs.

The table covers all 132 authenticated external route registrations, including 50 non-GET registrations under /api/v1 and the OpenAI and Anthropic compatibility routes. GET /api/v1/auth/validate and GET /api/v1/auth/whoami only validate or report the authenticated identity. They do not access tenant resources and require no resource grant. Native chat routes are registered only when chat is enabled.

OIDC callers retain the trusted-subject allowlist and namespace policy. Transaction tokens retain their off, audit, and enforce scope and tctx policies. Neither identity is converted into a TokenReview identity for these checks. Existing route-specific restrictions still apply. See Security and Transaction Token integration.

Health probes, static UI files, Gateway adapter ingress, GitHub webhooks, and internal worker/ACP routes are outside this inventory. Their existing probe, Gateway-bound Secret, HMAC, or internal credential policies are unchanged.

Resources and compound operations

CRD operations use the actual Kubernetes resource and CRUD verb. Stored records use namespaced virtual resources in SubjectAccessReview requests. They need RBAC rules, but no new CRDs. resource/subresource notation maps to separate SAR resource and subresource fields and to the same slash-separated string in RBAC.

  • sessions, memories, and memoryproposals authorize their stored records. Proposal review and apply are distinct custom verbs on memoryproposals, not CRUD updates. Applying also requires create on memories. Returning an already-applied memory requires get on its final referenced memory ID.
  • chats with create authorizes provider invocation and chat-session work. chats/config with get authorizes configuration reads. Nested tools require their own permissions for actual Task, Agent, Secret, and other resource operations, plus any applicable Gateway checks. A chat grant alone does not grant those tool operations. Supplying sessionId to native chat also requires get and update on that sessions name before reading history or writing the session. Supplying agentRef requires get on that agents name. Both use the resolved chat namespace. A newly generated chat session is covered by create chats.
  • repositoryscans/threatmodel, repositoryscans/scans, repositoryscans/slices, repositoryscans/droppedfindings, and repositoryscans/findings authorize stored data or actions for one named RepositoryScan. securityfindings and its subresources use the finding ID. Validation and patch action grants include their fixed pending/proposal state transitions. General finding mutations, such as dismiss and reopen, require update on securityfindings.
  • repositorymonitors/runs, repositorymonitors/items, and repositorymonitors/commands use the parent monitor name. monitorcommands, monitoractions, monitorworkactions, monitorimplementationjobs, monitormutations, and monitorevents authorize the corresponding stored ledgers. Their collection filters do not narrow a namespace-wide list grant.
  • gatewayevents and gatewaydeliveries are virtual resources in gateway.orka.ai. Their permissions are additional to the current Gateway identity and access checks.

The HTTP method does not define the permission. PUT /api/v1/agents/:name patches the Kubernetes Agent, so it requires patch on agents. Approval decisions require update on tasks/approvals and patch on the parent Task. A monitor run or command also needs patch on its RepositoryMonitor. Commands also require named create on repositorymonitors/runs before the command or its derived run is stored. A scan, validation, or patch request that creates a Task also needs create on tasks before any work is queued. Starting a scan also requires list on Tasks to check for active scan work. POST /api/v1/security/findings/:id/pull-request only returns a stored PR receipt, so its permission is get, despite the HTTP method.

The create_agent tool with initialPrompt requires Agent and Task creation plus named delete on the new Agent before creating it. Deletion permits rollback if the subsequent Task authorization fails. Creating an Agent without an initial Task requires only Agent creation.

Creating or updating a RepositoryMonitor also requires named get on the Agents used by its enabled workflows and on each credential Secret read during validation. Agent references use their explicit namespace or the monitor namespace. Credential Secrets use the monitor namespace; readCredentialRef takes precedence over the legacy gitSecretRef.

Scanner policies selected by customScanInstructionsRef and falsePositivePolicyRef require named get on core configmaps in the RepositoryScan namespace. Configuration create/update, scan start, and finding validation check these grants before loading the policies. The policy opt-in label or annotation remains required.

Workspace-class use remains a separate check for every authenticated identity, including OIDC and transaction-token callers. When a Task or workspace-backed Tool references a class, grant use on executionworkspaceclasses in workspace.orka.ai, with the selected class name and the Task or Tool namespace.

A Task with spec.execution.workspace.restoreFrom also requires use on executionworkspacecheckpoints in workspace.orka.ai, with the referenced checkpoint name and the Task namespace. This includes forks that supply executionCheckpoint. The workspace-class grant is still required; permission to read a checkpoint or create Tasks does not grant checkpoint use.

Gateway-owned Tasks and ledger records retain their current namespace/Gateway UID checks. General session APIs continue to exclude Gateway-owned sessions.

Namespace and name sources

The namespace column identifies the explicit request field. The first non-empty field wins where two fields are listed. Other request fields do not override it.

CodeExplicit namespace source
Qnamespace query parameter
CJSON namespace, then JSON metadata.namespace; query ignored
MJSON metadata.namespace; top-level namespace and query ignored
BJSON namespace; query and metadata ignored
QBnamespace query parameter, then JSON namespace
BQJSON namespace, then namespace query parameter
clusterEmpty SAR namespace; GatewayClass is cluster-scoped

A configured --watch-namespace is the only allowed target. An explicit mismatch returns 403; omitting an explicit namespace selects the watched namespace. Without a watch namespace, resolution is explicit namespace, then the identity's namespace, then default. With --enforce-namespace-isolation, the identity must carry a namespace and it must match the resolved namespace. Resolving a namespace does not itself grant access to it.

The name column is the SAR name, taken from the indicated URL parameter. empty means no name, including Kubernetes collection creates. A named virtual action uses the parent name or record ID even when its verb is create or list. Proposal review, apply, and archive trim the proposal ID to match store lookup; noncanonical namespaces for these actions are rejected.

Route permissions

All additional permissions use the row's resolved namespace unless specified otherwise. In the additional-checks column:

  • Gateway read means get on gateway.orka.ai/gateways, using the Gateway's bound namespace and name, when the Task is Gateway-owned. Task collection responses filter out Gateway-owned records the caller cannot read.
  • Gateway operate requires that same get plus update on the bound Gateway.
  • Class use means the conditional workspace-class check described above, using the final Task or Tool configuration.
  • Checkpoint use means the additional named checkpoint check described above when the final Task configuration includes execution.workspace.restoreFrom.
MethodPathAPI groupResource/subresourceVerbNameNamespaceAdditional checks
POST/api/v1/taskscore.orka.aitaskscreateemptyCClass use; Checkpoint use
GET/api/v1/usagecore.orka.aitaskslistemptyQlist on repositorymonitors and sessions; all selected team namespaces; Gateway read
GET/api/v1/usage/work/:idcore.orka.aitaskslistemptyQSame permissions and retained-Task access checks as the summary
GET/api/v1/usage/other/:categorycore.orka.aitaskslistemptyQSame permissions and retained-Task access checks as the summary
GET/api/v1/taskscore.orka.aitaskslistemptyQGateway read
GET/api/v1/tasks/:idcore.orka.aitasksget:idQGateway read
DELETE/api/v1/tasks/:idcore.orka.aitasksdelete:idQGateway operate
GET/api/v1/tasks/:id/logscore.orka.aitasksget:idQGateway read
GET/api/v1/tasks/:id/eventscore.orka.aitasksget:idQGateway read
GET/api/v1/tasks/:id/streamcore.orka.aitasksget:idQGateway read
GET/api/v1/tasks/:id/tracecore.orka.aitasksget:idQGateway read
GET/api/v1/tasks/:id/approvalscore.orka.aitasksget:idQGateway read
POST/api/v1/tasks/:id/approvals/:approvalID/decisioncore.orka.aitasks/approvalsupdate:idQpatch on core.orka.ai/tasks, :id; Gateway operate
POST/api/v1/tasks/:id/forkcore.orka.aitasksget:idQcreate on core.orka.ai/tasks, empty name; Gateway read; Class use; Checkpoint use
GET/api/v1/tasks/:id/resultcore.orka.aitasksget:idQGateway read
GET/api/v1/tasks/:id/plancore.orka.aitasksget:idQGateway read
GET/api/v1/tasks/:id/childrencore.orka.aitasksget:idQlist on core.orka.ai/tasks, empty name; Gateway read
GET/api/v1/tasks/:id/artifactscore.orka.aitasksget:idQGateway read
GET/api/v1/tasks/:id/artifacts/:filenamecore.orka.aitasksget:idQGateway read
GET/api/v1/sessionscore.orka.aisessionslistemptyQnone
GET/api/v1/sessions/:idcore.orka.aisessionsget:idQnone
GET/api/v1/sessions/:id/eventscore.orka.aisessionsget:idQnone
GET/api/v1/sessions/:id/streamcore.orka.aisessionsget:idQnone
DELETE/api/v1/sessions/:idcore.orka.aisessionsdelete:idQnone
GET/api/v1/gatewayclassesgateway.orka.aigatewayclasseslistemptyclusternone
GET/api/v1/gatewayclasses/:namegateway.orka.aigatewayclassesget:nameclusternone
GET/api/v1/gatewaysgateway.orka.aigatewayslistemptyQnone
GET/api/v1/gateways/:namegateway.orka.aigatewaysget:nameQnone
GET/api/v1/gatewaybindingsgateway.orka.aigatewaybindingslistemptyQnone
GET/api/v1/gatewaybindings/:namegateway.orka.aigatewaybindingsget:nameQnone
GET/api/v1/gateway-eventsgateway.orka.aigatewayeventslistemptyQget gateway.orka.ai/gateways with gateway query name, or unnamed list gateways without it
GET/api/v1/gateway-events/:idgateway.orka.aigatewayeventsget:idQget gateway.orka.ai/gateways with record Gateway name
GET/api/v1/gateway-deliveriesgateway.orka.aigatewaydeliverieslistemptyQget gateway.orka.ai/gateways with gateway query name, or unnamed list gateways without it
GET/api/v1/gateway-deliveries/:idgateway.orka.aigatewaydeliveriesget:idQget gateway.orka.ai/gateways with record Gateway name
POST/api/v1/gateway-deliveries/:id/retrygateway.orka.aigatewaydeliveriesupdate:idQget and update on gateway.orka.ai/gateways with record Gateway name
GET/api/v1/memoriescore.orka.aimemorieslistemptyQnone
POST/api/v1/memoriescore.orka.aimemoriescreateemptyBnone
GET/api/v1/memories/:idcore.orka.aimemoriesget:idQnone
PUT/api/v1/memories/:idcore.orka.aimemoriesupdate:idQBnone
DELETE/api/v1/memories/:idcore.orka.aimemoriesdelete:idQnone
POST/api/v1/memories/:id/disablecore.orka.aimemoriesupdate:idQnone
POST/api/v1/memories/:id/enablecore.orka.aimemoriesupdate:idQnone
GET/api/v1/memory-proposalscore.orka.aimemoryproposalslistemptyQnone
POST/api/v1/memory-proposalscore.orka.aimemoryproposalscreateemptyBnone
GET/api/v1/memory-proposals/:idcore.orka.aimemoryproposalsget:idQnone
POST/api/v1/memory-proposals/:id/reviewcore.orka.aimemoryproposalsreview:id, trimmedBQnone
POST/api/v1/memory-proposals/:id/applycore.orka.aimemoryproposalsapply:id, trimmedBQcreate on core.orka.ai/memories, empty name; If already applied, get core.orka.ai/memories with final referenced memory ID
POST/api/v1/memory-proposals/:id/archivecore.orka.aimemoryproposalsupdate:id, trimmedQnone
GET/api/v1/providerscore.orka.aiproviderslistemptyQnone
POST/api/v1/providerscore.orka.aiproviderscreateemptyCnone
GET/api/v1/providers/:namecore.orka.aiprovidersget:nameQnone
PUT/api/v1/providers/:namecore.orka.aiprovidersupdate:nameQnone
DELETE/api/v1/providers/:namecore.orka.aiprovidersdelete:nameQnone
GET/api/v1/toolscore.orka.aitoolslistemptyQnone
POST/api/v1/toolscore.orka.aitoolscreateemptyCClass use
GET/api/v1/tools/:namecore.orka.aitoolsget:nameQnone
PUT/api/v1/tools/:namecore.orka.aitoolsupdate:nameQClass use
DELETE/api/v1/tools/:namecore.orka.aitoolsdelete:nameQnone
GET/api/v1/runtime-poolscore.orka.airuntimepoolslistemptyQnone
GET/api/v1/runtime-pools/:namecore.orka.airuntimepoolsget:nameQnone
GET/api/v1/agent-runtimescore.orka.aiagentruntimeslistemptyQnone
POST/api/v1/agent-runtimescore.orka.aiagentruntimescreateemptyMnone
GET/api/v1/agent-runtimes/:namecore.orka.aiagentruntimesget:nameQnone
PUT/api/v1/agent-runtimes/:namecore.orka.aiagentruntimesupdate:nameQnone
DELETE/api/v1/agent-runtimes/:namecore.orka.aiagentruntimesdelete:nameQnone
POST/api/v1/agentscore.orka.aiagentscreateemptyCnone
GET/api/v1/agentscore.orka.aiagentslistemptyQnone
GET/api/v1/agents/:namecore.orka.aiagentsget:nameQnone
PUT/api/v1/agents/:namecore.orka.aiagentspatch:nameQnone
DELETE/api/v1/agents/:namecore.orka.aiagentsdelete:nameQnone
POST/api/v1/skillscore.orka.aiskillscreateemptyCnone
GET/api/v1/skillscore.orka.aiskillslistemptyQnone
GET/api/v1/skills/:namecore.orka.aiskillsget:nameQnone
GET/api/v1/skills/:name/contentcore.orka.aiskillsget:nameQnone
PUT/api/v1/skills/:namecore.orka.aiskillsupdate:nameQnone
DELETE/api/v1/skills/:namecore.orka.aiskillsdelete:nameQnone
POST/api/v1/security/repositoriescore.orka.airepositoryscanscreateemptyCNamed get on core configmaps for configured scanner-policy references
GET/api/v1/security/repositoriescore.orka.airepositoryscanslistemptyQnone
GET/api/v1/security/repositories/:namecore.orka.airepositoryscansget:nameQnone
PUT/api/v1/security/repositories/:namecore.orka.airepositoryscansupdate:nameQNamed get on core configmaps for configured scanner-policy references
DELETE/api/v1/security/repositories/:namecore.orka.airepositoryscansdelete:nameQnone
GET/api/v1/security/repositories/:name/threat-modelcore.orka.airepositoryscans/threatmodelget:nameQnone
PUT/api/v1/security/repositories/:name/threat-modelcore.orka.airepositoryscans/threatmodelupdate:nameQnone
GET/api/v1/security/repositories/:name/scanscore.orka.airepositoryscans/scanslist:nameQnone
POST/api/v1/security/repositories/:name/scanscore.orka.airepositoryscans/scanscreate:nameQlist, create, and delete on core.orka.ai/tasks, empty name; patch on core.orka.ai/repositoryscans and core.orka.ai/repositoryscans/status, :name; Class use; named get on core configmaps for configured scanner-policy references
GET/api/v1/security/repositories/:name/slicescore.orka.airepositoryscans/sliceslist:nameQnone
GET/api/v1/security/repositories/:name/slices/:sliceIDcore.orka.airepositoryscans/slicesget:nameQnone
GET/api/v1/security/repositories/:name/dropped-findingscore.orka.airepositoryscans/droppedfindingslist:nameQnone
GET/api/v1/security/repositories/:name/findingscore.orka.airepositoryscans/findingslist:nameQnone
GET/api/v1/security/findings/:idcore.orka.aisecurityfindingsget:idQnone
POST/api/v1/security/findings/:id/dismisscore.orka.aisecurityfindingsupdate:idQnone
POST/api/v1/security/findings/:id/reopencore.orka.aisecurityfindingsupdate:idQnone
POST/api/v1/security/findings/:id/validatecore.orka.aisecurityfindings/validationcreate:idQcreate on core.orka.ai/tasks, empty name; Class use; named get on core configmaps for configured scanner-policy references
POST/api/v1/security/findings/:id/patchcore.orka.aisecurityfindings/patchescreate:idQcreate on core.orka.ai/tasks, empty name; Class use
GET/api/v1/security/findings/:id/patchescore.orka.aisecurityfindings/patcheslist:idQnone
POST/api/v1/security/findings/:id/pull-requestcore.orka.aisecurityfindings/pullrequestget:idQStored PR receipt read only
POST/api/v1/monitors/repositoriescore.orka.airepositorymonitorscreateemptyCNamed get on the core.orka.ai/agents and core secrets references read during validation
GET/api/v1/monitors/repositoriescore.orka.airepositorymonitorslistemptyQnone
GET/api/v1/monitors/repositories/:namecore.orka.airepositorymonitorsget:nameQnone
PUT/api/v1/monitors/repositories/:namecore.orka.airepositorymonitorsupdate:nameQNamed get on the core.orka.ai/agents and core secrets references read during validation
DELETE/api/v1/monitors/repositories/:namecore.orka.airepositorymonitorsdelete:nameQnone
POST/api/v1/monitors/repositories/:name/runscore.orka.airepositorymonitors/runscreate:nameQpatch on core.orka.ai/repositorymonitors, :name
GET/api/v1/monitors/repositories/:name/runscore.orka.airepositorymonitors/runslist:nameQnone
GET/api/v1/monitors/repositories/:name/itemscore.orka.airepositorymonitors/itemslist:nameQnone
POST/api/v1/monitors/repositories/:name/commandscore.orka.airepositorymonitors/commandscreate:nameQcreate on core.orka.ai/repositorymonitors/runs, :name; patch on core.orka.ai/repositorymonitors, :name
GET/api/v1/monitors/commandscore.orka.aimonitorcommandslistemptyQnone
GET/api/v1/monitors/commands/:idcore.orka.aimonitorcommandsget:idQnone
GET/api/v1/monitors/actionscore.orka.aimonitoractionslistemptyQnone
GET/api/v1/monitors/actions/:idcore.orka.aimonitoractionsget:idQnone
GET/api/v1/monitors/work-actionscore.orka.aimonitorworkactionslistemptyQnone
GET/api/v1/monitors/work-actions/:idcore.orka.aimonitorworkactionsget:idQnone
GET/api/v1/monitors/implementation-jobscore.orka.aimonitorimplementationjobslistemptyQnone
GET/api/v1/monitors/implementation-jobs/:idcore.orka.aimonitorimplementationjobsget:idQnone
GET/api/v1/monitors/implementation-jobs/:id/patch-previewcore.orka.aimonitorimplementationjobsget:idQnone
GET/api/v1/monitors/mutationscore.orka.aimonitormutationslistemptyQnone
GET/api/v1/monitors/mutations/:idcore.orka.aimonitormutationsget:idQnone
GET/api/v1/monitors/eventscore.orka.aimonitoreventslistemptyQnone
GET/api/v1/substrate-actor-poolscore.orka.aisubstrateactorpoolslistemptyQnone
POST/api/v1/substrate-actor-poolscore.orka.aisubstrateactorpoolscreateemptyCnone
GET/api/v1/substrate-actor-pools/:namecore.orka.aisubstrateactorpoolsget:nameQnone
PUT/api/v1/substrate-actor-pools/:namecore.orka.aisubstrateactorpoolsupdate:nameQnone
DELETE/api/v1/substrate-actor-pools/:namecore.orka.aisubstrateactorpoolsdelete:nameQnone
GET/api/v1/auth/validatenonenoneidentity onlyemptynot applicableAuthenticated identity only
GET/api/v1/auth/whoaminonenoneidentity onlyemptynot applicableAuthenticated identity only
GET/api/v1/secrets""secretslistemptyQnone
POST/api/v1/chatcore.orka.aichatscreateemptyBSupplied JSON sessionId: get and update on core.orka.ai/sessions with that name; supplied JSON agentRef: get core.orka.ai/agents with that name; each nested tool requires its own resource permissions
GET/api/v1/chat/configcore.orka.aichats/configgetemptyQnone
DELETE/api/v1/chat/:sessionIdcore.orka.aisessionsdelete:sessionIdQnone
POST/openai/v1/chat/completionscore.orka.aichatscreateemptyQEach nested tool requires its own resource permissions; custom Tool metadata requires unnamed list core.orka.ai/tools and is omitted on denial
GET/openai/v1/modelscore.orka.aiproviderslistemptyQnone
POST/anthropic/v1/messagescore.orka.aichatscreateemptyQEach nested tool requires its own resource permissions; custom Tool metadata requires unnamed list core.orka.ai/tools and is omitted on denial
GET/anthropic/v1/modelscore.orka.aiproviderslistemptyQnone

Grant access

Use a RoleBinding in the installation namespace. A ClusterRoleBinding would grant namespaced permissions across the cluster. The Kustomize bundle includes orka-api-viewer-role and orka-api-editor-role. The viewer covers the read permissions above. The editor adds the listed Orka mutations. Nested tools that run Kubernetes workloads need separate workload grants, including get on pods/log when reading Pod logs. Neither helper grants Secrets, ConfigMaps, or workspace-class use. Kubernetes code_exec preflights create and delete on its temporary Secrets, ServiceAccounts, Jobs, and optional NetworkPolicies before creating any of them. The delete permissions cover cleanup after completion or failed setup. ConfigMap caching is optional. A denied cache lookup behaves as a cache miss; without ConfigMap create, execution returns its result without storing it. The source files in config/rbac have unprefixed names when applied directly.

TokenReview calls to GitHub tools require a Task repository scope and a referenced credential. Grant named get on the Task and Secret. check_pull_request_ci uses the selected repository's read credential; create_pull_request uses forgeCredentialRef. These calls use only the Task's repository and credentials.

Optional Git credential discovery skips Secret names that the caller cannot read. Other lookup errors still stop discovery, and explicit Secret reads remain subject to authorization.

Existing Task helper roles retain session access; Task editor/admin roles also grant update on tasks/approvals. Gateway helper roles include ledger reads; Gateway editor/admin roles also grant delivery retry. GatewayClass reads require a cluster-scoped binding, such as the existing orka-gatewayclass-viewer-role. A namespaced RoleBinding cannot grant those cluster-scoped reads.

The Helm client Role includes virtual reads, chat creation and continuation, approval decisions, and monitor actions alongside its existing resource grants. Memory review/apply, security mutations, and Gateway retry require separate operator grants. Its read-only Agent, Tool, and Provider permissions do not authorize nested chat tools to mutate those resources.

For example, bind the installed API viewer to an existing ServiceAccount:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: api-reader
namespace: team-a
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: orka-api-viewer-role
subjects:
- kind: ServiceAccount
name: api-client
namespace: team-a

For a narrower grant, this Role allows an existing ServiceAccount to read, review, and apply one proposal. The separate unnamed create memories grant is required for apply. Reading an already-applied memory also needs get memories with that memory's ID; add that grant only for records the caller may read.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: apply-selected-proposal
namespace: team-a
rules:
- apiGroups: [core.orka.ai]
resources: [memoryproposals]
resourceNames: [proposal-123]
verbs: [get, review, apply]
- apiGroups: [core.orka.ai]
resources: [memories]
verbs: [create]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: apply-selected-proposal
namespace: team-a
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: apply-selected-proposal
subjects:
- kind: ServiceAccount
name: memory-reviewer
namespace: team-a

GET /api/v1/secrets returns names only, but it requires actual core Kubernetes list access on secrets. That same grant permits listing Secret objects through the Kubernetes API, including their data. Keep it separate from general viewer roles. The API group is the empty string, not core.orka.ai.

Grant workspace-class use with an explicit resourceNames entry on a Role in the Task or Tool namespace. Broader API edit permission does not substitute for it. For restores, add a rule for the selected checkpoint. This example grants both class use and use of the before-refactor checkpoint:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: use-reviewed-workspace
namespace: team-a
rules:
- apiGroups: [workspace.orka.ai]
resources: [executionworkspaceclasses]
resourceNames: [reviewed-workspace]
verbs: [use]
- apiGroups: [workspace.orka.ai]
resources: [executionworkspacecheckpoints]
resourceNames: [before-refactor]
verbs: [use]

Bind this Role to the caller with a RoleBinding in team-a, as above. Check permissions using the same resource, namespace, verb, and name as the route. A successful /auth/validate or /auth/whoami response proves authentication only.