Authentication
Almost every endpoint requires authentication (NATIVE_WAZUH_REQUIRE_AUTH=true is the default). The API accepts four mechanisms — pick whichever fits your use case.
Supported methods
| Method | Header | Typical use |
|---|---|---|
| API key | X-API-Key: rfk_… or Authorization: Bearer rfk_… | Integrations, scripts, CI/CD. |
| JWT Bearer | Authorization: Bearer <jwt> | SPAs, user agents after login. |
| Session cookie | Cookie: ruleforge_session=<jwt> | Browser hitting RuleForge itself. |
| OIDC/SSO | 3-legged flow, results in cookie/JWT | Organizations with a corporate IdP. |
Rule of thumb: for anything that isn't a browser, use API keys. The lifecycle is simpler, scopes are granular and don't depend on session state.
API keys
Format
Every RuleForge API key starts with the rfk_ prefix followed by a random string. Example:
rfk_01HS2XA9CKEB7WZG8RMN2KPQV7_e6c4f2…
How to get one
A key is created by an organization admin in the UI (Settings → API keys) or via API through POST /platform/organizations/{organization_id}/api-keys. The full value appears only once at creation time — store it in a secrets manager.
See the operational guide: API keys.
How to send it
The API accepts the secret in two headers — use one of the two:
X-API-Key: rfk_01HS2XA9CKEB7WZG8RMN2KPQV7_e6c4f2…
or
Authorization: Bearer rfk_01HS2XA9CKEB7WZG8RMN2KPQV7_e6c4f2…
If the key belongs to a user who is also a member of other organizations, send X-Org-Id to pin the context. For API keys this is usually unnecessary — the key is already scoped to an organization.
Scopes
Each key has a set of scopes that restrict what it can do. Pick the smallest set sufficient for the integration.
| Scope | Internal permissions |
|---|---|
projects:read | View projects, workspaces, versions. |
projects:write | projects:read + create/edit projects. |
rulesets:read | Read rulesets for a project. |
rulesets:write | rulesets:read + create workspaces and edit content. |
analysis:run | Run POST /analysis/validate, logtest-native, full. |
cases:read | List and run test cases. |
cases:write | cases:read + create/edit cases. |
reviews:read | List reviews. |
reviews:write | Open reviews, comment, approve. |
versions:read | List versions. |
versions:write | Create and publish versions. |
admin:* | Full org set (including members and integrations). Use only in controlled administrative automations. |
If the key lacks the required scope, the response is 403 forbidden with code="forbidden" and a message indicating insufficient scope.
Lifecycle
- Expiration: optional. Always use it when temporary.
- Rotation:
POST …/api-keys/{id}/rotate— generates a new secret, keeps the ID. The old key is invalidated immediately. - Revocation:
POST …/api-keys/{id}/revoke— ends access. Irreversible.
All actions are audited in the organization.
Project restriction
A key can be created with project_id — in that case it only authorizes operations on the given project. Requests on other projects return 403.
JWT Bearer
JWT is issued by POST /auth/login:
{
"access_token": "<jwt>",
"token_type": "bearer",
"expires_at": "2026-04-25T03:30:00Z",
"user": { "...": "..." }
}
Send the token on every subsequent request:
Authorization: Bearer <jwt>
- Default TTL: 480 minutes (8 hours), controlled by
NATIVE_WAZUH_JWT_TTL_MINUTES. - Refresh: log in again — there is no refresh token at this time.
- Revocation:
POST /auth/logoutinvalidates thejtion the server; the cookie is cleared and the JWT stops being accepted even before it expires.
RuleForge JWTs do not start with
rfk_. If your token starts withrfk_you are using an API key — send it in theX-API-Keyheader or accept thatBearer rfk_…is treated as an API key by the server.
Session cookie
When login happens in a browser, the server sets the cookie:
Set-Cookie: ruleforge_session=<jwt>; HttpOnly; SameSite=Lax; Path=/
Flags controlled by envs:
NATIVE_WAZUH_SESSION_COOKIE_NAME(defaultruleforge_session)NATIVE_WAZUH_SESSION_COOKIE_SECURE(defaultfalse; true on HTTPS production)NATIVE_WAZUH_SESSION_COOKIE_SAMESITE(lax,strictornone)NATIVE_WAZUH_SESSION_TTL_SECONDS(default 900 s — periodic revalidation by the frontend)
Use cookies only when the client is a browser. In scripts, prefer API keys or an explicit JWT.
Corporate OIDC / SSO
Organizations with a corporate IdP (Okta, Entra ID, Google Workspace, Keycloak) may delegate login:
- User navigates to
GET /sso/oidc/{provider_id}/login. - RuleForge redirects to the IdP.
- IdP calls back to
GET /sso/oidc/callback. - A session is created (cookie) and the user is redirected to the app.
Endpoint details at SSO and operational configuration.
Roles and permissions (user session)
For authenticated users (JWT/cookie), permission comes from the role in the organization (and project-specific roles). Summary:
| Role | What they can do |
|---|---|
org_admin | Everything in the organization, including members, integrations and identity providers. |
security_content_lead | Content management + approvals. |
engineer | Create and edit content, run analysis, open reviews and versions. |
reviewer | View content, comment, approve. |
read_only | View and run analysis. |
See Roles and permissions for details and the full matrix. For API keys what matters is the scope — roles don't apply.
Unauthenticated requests
Some endpoints are open by design:
GET /api/v1/health— liveness.GET /api/v1/product/status— public product status.GET /healthz— readiness probe (not under/api/v1).POST /auth/bootstrap— one-time, when the platform is blank.POST /auth/login— submit credentials, receive JWT.GET /billing/plans/public— list publicly visible plans.- Inbound webhook endpoints (
/webhooks/git/*,/billing/webhooks/stripe) — authenticated via HMAC signature.
Everything else requires authentication.
Authentication errors
| Code | When it happens |
|---|---|
401 unauthorized | Missing, invalid, expired or revoked token/key. |
403 forbidden | Authenticated but lacking permission (insufficient role or scope, organization out of context). |
429 rate_limited | Too many login or registration attempts from the same IP/email. |
Responses follow the standard error envelope. Generate a new key/session before retrying on 401.