# Authenticate tools

Give a tool backend the credential it needs by setting the handler-level `auth`
stanza on a ToolRegistry handler. The dashboard does not expose this stanza, so
you set it in the manifest.

`auth` sits beside `httpConfig`, `openAPIConfig`, `grpcConfig` and `mcpConfig`,
so the same shape works on every handler type. The runtime sends the credential
as an HTTP `Authorization` header, gRPC `authorization` metadata or an MCP
transport header.

## Prerequisites

- A ToolRegistry with at least one `http`, `openapi`, `grpc` or `mcp` handler. A `platform` handler cannot set `auth`.
- For `bearer` or `basic`, a Secret holding the credential in the same namespace as the AgentRuntime.
- An AgentRuntime that references the ToolRegistry through `toolRegistryRef`.

## Auth types

| `auth.type` | Credential | Sent as |
|-------------|-----------|---------|
| `none` (default) | none | no credential |
| `bearer` | `secretRef` value | `Authorization: Bearer <token>` |
| `basic` | `secretRef` value `user:password` | `Authorization: Basic <base64>` |
| `serviceAccount` | projected, audience-bound ServiceAccount token | `Authorization: Bearer <token>` |
| `workloadIdentity` | the pod's ambient Azure identity, with no stored secret | `Authorization: Bearer <token>`, or the custom `header` |

See the [ToolRegistry reference](/reference/core/toolregistry/) for every `auth` field.

## Bearer / basic token

Create the credential Secret and reference it from the handler:

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: weather-credentials
  namespace: agents        # same namespace as the AgentRuntime
type: Opaque
stringData:
  token: "sk-live-abc123"  # illustrative value; for basic auth, use "username:password"
---
apiVersion: omnia.altairalabs.ai/v1alpha1
kind: ToolRegistry
metadata:
  name: weather-tools
  namespace: agents
spec:
  handlers:
    - name: weather
      type: http
      httpConfig:
        endpoint: https://api.weather.example/v1/forecast
        method: GET
      auth:
        type: bearer
        secretRef:
          name: weather-credentials
          key: token
      tool:
        name: get_weather
        description: "Get the weather forecast for a city"
        inputSchema:
          type: object
          properties:
            city: { type: string }
          required: [city]
```

Apply both and let the AgentRuntime reconcile.

The operator copies each handler's `secretRef` value into a Secret named
`<agentruntime-name>-tool-secrets`, with one key per authenticated handler (the
key is the handler name). It mounts that Secret read-only into the runtime at
`/etc/omnia/tool-secrets`. The tools ConfigMap holds only the path, for example
`authTokenPath: /etc/omnia/tool-secrets/weather`, never the value.

If the Secret or key is missing, the AgentRuntime reconcile fails and the
runtime does not start with a broken config. To send a second secret header,
use [`headersFromSecret`](/how-to/tools/advanced-http-tools/#source-a-secret-header).

### Verify

```bash
# The operator-managed companion Secret exists, keyed by handler name:
kubectl describe secret <agentruntime>-tool-secrets -n agents

# The tools ConfigMap references the token by path, not value:
kubectl get configmap <agentruntime>-tools -n agents -o yaml | grep authTokenPath

# The runtime Deployment mounts the companion Secret:
kubectl get deployment <agentruntime> -n agents \
  -o jsonpath='{.spec.template.spec.volumes[*].secret.secretName}'
```

## ServiceAccount token (`serviceAccount`)

For an in-cluster backend that can validate a Kubernetes token, send an
audience-bound projected ServiceAccount token. The operator projects the token
into the runtime and the backend validates it through the TokenReview API. No
long-lived secret is stored.

```yaml
- name: internal-api
  type: http
  httpConfig:
    endpoint: http://internal.svc.cluster.local/api
  auth:
    type: serviceAccount
    serviceAccount:
      audience: internal-api      # required; the backend checks the token against this audience
  tool:
    name: internal_action
    description: "Call the internal API"
    inputSchema:
      type: object
```

### Backend validation

The backend must validate the incoming `Authorization: Bearer <token>` with the
Kubernetes TokenReview API and check that the token's audience matches
`serviceAccount.audience`. Its own ServiceAccount needs permission to `create`
`tokenreviews` in the `authentication.k8s.io` API group. TokenReview is
cluster-scoped, so grant it with a ClusterRole:

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: internal-api-tokenreview
rules:
  - apiGroups: [authentication.k8s.io]
    resources: [tokenreviews]
    verbs: [create]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: internal-api-tokenreview
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: internal-api-tokenreview
subjects:
  - kind: ServiceAccount
    name: internal-api            # the backend's own ServiceAccount
    namespace: agents
```

The operator projects the token with a one-hour lifetime at
`/var/run/secrets/omnia/tool-sa/<handler>/token`. The kubelet rotates it before
it expires and the runtime re-reads the file on every tool call.

## workloadIdentity (Azure)

`auth.type: workloadIdentity` authenticates a tool call as the agent pod's
ambient Azure identity, so Omnia stores no credential. `cloud` accepts `azure`
only. The type works on `http`, `grpc`, `openapi` and `mcp` handlers.

```yaml
- name: graph-api
  type: http
  httpConfig:
    endpoint: https://graph.microsoft.com/v1.0/me
    method: GET
  auth:
    type: workloadIdentity
    workloadIdentity:
      cloud: azure
      audience: "https://graph.microsoft.com"  # required; the runtime requests <audience>/.default
      # header: Authorization                  # optional; default Authorization
  tool:
    name: whoami
    description: "Read the caller's Graph profile"
    inputSchema:
      type: object
```

The runtime gets a token for `audience` through `DefaultAzureCredential`,
resolving it on every call from a cache that refreshes five minutes before
expiry, and sets it on `header` as `Bearer <token>`. If it cannot get a token,
that tool call fails instead of reaching the backend unauthenticated.

### Cluster setup

The identity is the same one keyless [Azure provider auth](/reference/core/provider/)
uses. Set it up once per agent identity:

1. Label the agent pod `azure.workload.identity/use: "true"` and run it as a
   ServiceAccount annotated with `azure.workload.identity/client-id: <client-id>`.
   Set both for every agent in a workspace with `spec.runtime.podLabels` and
   `spec.runtime.serviceAccountName` on the Workspace. An AgentRuntime that sets
   `spec.podOverrides.serviceAccountName` opts out of both.
2. Create a federated identity credential on the Entra ID app or user-assigned
   managed identity. Trust the cluster's OIDC issuer and the subject
   `system:serviceaccount:<namespace>:<serviceAccount>`.
3. Grant the identity access to the API of every `workloadIdentity` tool. The pod
   has one identity, shared with the model provider.

## Migrating off the deprecated fields

`httpConfig.authType` and `httpConfig.authSecretRef`, and their `openAPIConfig`
equivalents, are deprecated. They still work and are normalized into `auth`, but
a handler that sets both `auth` and a legacy field is rejected. Move to `auth`:

```yaml
# before (deprecated)
httpConfig:
  endpoint: https://api.example/v1
  authType: bearer
  authSecretRef:
    name: creds
    key: token

# after
httpConfig:
  endpoint: https://api.example/v1
auth:
  type: bearer
  secretRef:
    name: creds
    key: token
```

## Related pages

- [ToolRegistry CRD reference](/reference/core/toolregistry/)
- [Advanced HTTP tools](/how-to/tools/advanced-http-tools/)
- [Build a tool backend](/how-to/tools/build-a-tool-backend/) — the request and response contract your backend implements
- [Configure tool policies](/how-to/security/configure-tool-policies/) — allow, deny and header injection on top of authenticated tools
- [Delegation tokens](/reference/platform/delegation-tokens/) — tell a tool which user and agent a call was made for