SSO endpoints
/sso/* group + test/validation routes on /platform/organizations/{id}/identity-providers/{id}/…. Supports OIDC (production) and SAML (preview).
Provider configuration is done via identity providers endpoints. The endpoints on this page execute the login/logout flow.
Endpoint table
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /sso/oidc/{provider_id}/login | Public | Starts OIDC flow. Returns the IdP URL. |
| POST | /sso/discovery | Public | Discovers the provider configured for an email/domain. |
| GET | /sso/oidc/callback | Public (validated via state) | Callback after authenticating at the IdP. |
| GET | /sso/saml/sp-config | Public | Service Provider config (to use on the IdP). |
| GET | /sso/saml/metadata | Public | SP metadata XML to import in the IdP. |
| POST | /sso/saml/acs | — | Currently returns 501 Not Implemented. |
| POST | /sso/logout | — | Clears the session cookie and redirects. |
| POST | /platform/organizations/{organization_id}/identity-providers/{provider_id}/test | Session | Triggers a test OIDC flow. |
| POST | /platform/organizations/{organization_id}/identity-providers/{provider_id}/validate | Session | Validates the config (discovery + token test) without running the full flow. |
OIDC flow (end user)
GET /sso/oidc/{provider_id}/login
Starts the login. Returns (JSON) the URL the frontend should redirect to.
Query params:
redirect_path— absolute path (starts with/) to return to after authenticating. Default: root.
Response 200:
{
"authorization_url": "https://idp.acme.com/oauth2/v1/authorize?client_id=…&state=…",
"state": "opaque-token"
}
The frontend redirects to authorization_url. The state is stored in Redis (TTL configurable via NATIVE_WAZUH_SSO_STATE_TTL_SECONDS, default 600 s) to protect against CSRF on the callback.
POST /sso/discovery
Given an email or domain, tells which identity provider is configured. Used by the login screen to decide whether to show "Continue with Okta", "Continue with Azure AD", etc.
Request body (SsoDiscoveryRequest):
{ "email": "alice@acme.com" }
Response 200 (SsoDiscoveryResponse):
{
"provider_id": "idp_...",
"name": "Corporate Okta",
"kind": "oidc",
"login_url": "/api/v1/sso/oidc/idp_.../login"
}
When no provider is linked to the domain, returns { "provider_id": null } — the frontend falls back to traditional login.
GET /sso/oidc/callback
Endpoint the IdP calls back after authentication. Do not call manually — invoked by the IdP's OAuth2/OIDC flow itself.
Query params (sent by the IdP):
provider_id— target provider (part of theredirect_uriregistered at the IdP).code— authorization code.state— token to validate CSRF.
Behavior:
- Validates
stateagainst what was stored in Redis. - Exchanges
codefor tokens at the IdP (token_endpointfrom discovery). - Reads the
userinfoorid_tokento extract email/claims. - Creates or links the user to the provider's organization.
- Opens a session (cookie).
- Redirects to
NATIVE_WAZUH_PUBLIC_BASE_URL+ originalredirect_path.
Response: 302 Found with Set-Cookie: ruleforge_session=….
POST /sso/logout
Clears the session cookie and redirects to the public home.
Response: 302 Found to NATIVE_WAZUH_PUBLIC_BASE_URL.
Currently does not trigger SLO (Single Logout) at the IdP — only clears the local session. SLO is tracked for future releases.
SAML (preview)
SAML is in preview. The endpoints below exist so IdPs can be configured, but processing of the SAMLResponse (ACS) is not implemented yet.
GET /sso/saml/sp-config
Response 200 (SamlServiceProviderConfig):
{
"entity_id": "https://ruleforge.example.com/sso/saml",
"acs_url": "https://ruleforge.example.com/api/v1/sso/saml/acs",
"slo_url": "https://ruleforge.example.com/api/v1/sso/logout",
"name_id_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
}
GET /sso/saml/metadata
Response 200 — Content-Type: application/samlmetadata+xml:
<?xml version="1.0" encoding="UTF-8"?>
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
entityID="https://ruleforge.example.com/sso/saml">
<SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
<AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://ruleforge.example.com/api/v1/sso/saml/acs"
index="1" isDefault="true" />
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://ruleforge.example.com/api/v1/sso/logout" />
</SPSSODescriptor>
</EntityDescriptor>
Import this XML in the SAML IdP to register RuleForge as an SP.
POST /sso/saml/acs
Response 501 — SAML is still in preview. The ACS endpoint is not yet available for production use.
Test and validate (admin)
POST /platform/organizations/{organization_id}/identity-providers/{provider_id}/test
Triggers a test OIDC flow without affecting real sessions — useful for debugging configuration.
Query params: redirect_path (optional).
Response 200: returns the IdP URL + a test state.
POST /platform/organizations/{organization_id}/identity-providers/{provider_id}/validate
Performs the following checks without UI:
- Resolves the
discovery_url. - Confirms
authorization_endpoint,token_endpointandjwks_urirespond. - Confirms the
client_idis accepted (optional, IdP-dependent).
Response 200:
{
"valid": true,
"issuer": "https://acme.okta.com",
"authorization_endpoint": "https://acme.okta.com/oauth2/v1/authorize",
"token_endpoint": "https://acme.okta.com/oauth2/v1/token",
"jwks_uri": "https://acme.okta.com/oauth2/v1/keys",
"warnings": [],
"errors": []
}
When valid=false, the errors list explains what failed.
Shared structures
SsoDiscoveryRequest / SsoDiscoveryResponse
// request
{ "email": "string" }
// response
{
"provider_id": "string|null",
"name": "string|null",
"kind": "oidc|saml|null",
"login_url": "string|null"
}
SamlServiceProviderConfig
{
"entity_id": "string",
"acs_url": "string",
"slo_url": "string",
"name_id_format": "string"
}