Authentication and tokens
Bairav uses four kinds of credential. Pick by what you are connecting.
| Credential | Looks like | Sent as | Used for | Created in |
|---|---|---|---|---|
| Session token | A signed token | Authorization: Bearer <token> | The Portal API | Sign-in |
| Gateway key | bst_<8 hex>.<secret> | In the URL path: /gw/{token}/v1/... | The AI Gateway | Console, OpenAI gateway, or POST /api/gateway/keys (admin) |
| Sensor token | bst_<8 hex>.<secret> | X-Sensor-Token header | Sensor events and governance | Settings, Sensor deployment, or POST /api/admin/enrollment-tokens (admin) |
| Ingest token | obt_<8 hex>.<secret> | X-Ingest-Token header, or Authorization: Bearer | Security events, OTLP traces, SBOMs | Settings, Security data and telemetry, or POST /api/security/ingest-tokens (manage_observability) |
Gateway keys and sensor tokens share the bst_ prefix but are different credentials in different tables. Do not use one where the other is expected.
How machine tokens are protected
- The part after the dot is the secret. The 8-character prefix is stored in clear as a lookup handle; the secret is stored only as an Argon2id hash.
- The full token is shown once, when you create it. It cannot be retrieved later. If you lose it, revoke it and create a new one.
- Revoking a token stops it working immediately. The record stays, marked revoked, for the audit trail.
- Repeated failed authentication against one token prefix is rate limited (30 a minute), then answered with
429. - Tokens are bound to one tenant. Ingest tokens are bound to one telemetry source.
Sign in to the Portal API
The only sign-in that works without a browser is a local account:
POST /api/auth/breakglass
Content-Type: application/json
{ "email": "svc-automation@example.com", "password": "..." }
{ "access_token": "...", "refresh_token": "...", "token_type": "bearer" }
| Token | Lifetime |
|---|---|
| Access token | 1 hour |
| Refresh token | 14 days |
Send the access token as Authorization: Bearer <access token>. Before it expires, call POST /api/auth/refresh with the refresh token; you get a new pair, so keep the new refresh token.
samples/auth/login.py
"""Sign in to the Bairav Portal API with a local account, then call it. Standard library only.
export BAIRAV_HOST=your-tenant.example.com
export BAIRAV_EMAIL=svc-automation@example.com
export BAIRAV_PASSWORD=... # use a dedicated account with the narrowest role that works
"""
import json
import os
import urllib.request
base = f"https://{os.environ['BAIRAV_HOST']}"
def call(method: str, path: str, body: dict | None = None, token: str | None = None) -> dict:
headers = {"Content-Type": "application/json"}
if token:
headers["Authorization"] = f"Bearer {token}"
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(base + path, data=data, method=method, headers=headers)
with urllib.request.urlopen(req, timeout=15) as resp:
return json.load(resp)
tokens = call("POST", "/api/auth/breakglass", {"email": os.environ["BAIRAV_EMAIL"], "password": os.environ["BAIRAV_PASSWORD"]})
access = tokens["access_token"] # valid for 1 hour; tokens["refresh_token"] is valid for 14 days
me = call("GET", "/api/auth/me", token=access)
print(me["email"], me["role"], me["tenant_name"])
print("permissions:", ", ".join(me["permissions"]))
# Renew before the hour is up. Always keep the NEW refresh token.
tokens = call("POST", "/api/auth/refresh", {"refresh_token": tokens["refresh_token"]})
If the same email and password exist in more than one tenant, sign-in fails with 401 rather than guessing.
Single sign-on with Microsoft Entra ID is for people using the console in a browser. It is not an API.
Keep credentials safe
- Use a dedicated account with the narrowest role that works for automation. Do not use a person's account.
- Keep secrets out of URLs where you can. The gateway key is part of the URL path by design, so the whole gateway base URL is a secret: it appears in access logs, proxies and shell history. Store it in a secret manager, never in source control.
- Never put a token in a front-end bundle. Call Bairav from a server.
- Rotate. Create the new token, deploy it, then revoke the old one.
- Tenant comes from the credential. Do not send a tenant ID; it is ignored or rejected.