EnterpriseSingle Sign-On

Self-managed OIDC

Point a Sentisec install you run yourself at your own OIDC provider โ€” realm setup, the workspace claim, role mapping, and what the mode does not cover.

A Sentisec install you run yourself can authenticate directly against your own OIDC provider. No Sentisec-operated service sits anywhere in the sign-in path โ€” which is the point, for sovereignty requirements and disconnected environments.

This page is written against Keycloak, because that is what such deployments usually already run. Nothing in it is Keycloak-specific beyond the console paths, so it applies unchanged to Zitadel, Authentik, Ping, or any compliant provider.

Warning
Self-managed installs only

Hosted Sentisec authenticates through our managed identity layer and cannot be pointed at your realm. For hosted tenants, your provider is wired as an upstream connection there instead โ€” see Single Sign-On.

How it works

  • The dashboard runs a standard authorization-code flow with PKCE against your realm, discovering every endpoint from the issuer. Nothing about your provider is hardcoded.
  • The resulting session lives in an encrypted cookie in the operator's browser โ€” it carries a bearer token, so it is encrypted, not merely signed.
  • The API verifies every token against your realm's published keys independently, rather than trusting the dashboard's word for it.
  • Workspace membership and roles come from a claim your realm emits.

1 ยท Realm and client

Pick or create a realm. Note its issuer URL โ€” https://<keycloak-host>/realms/<realm>. Everything else is discovered from it.

Create an OpenID Connect client with the standard flow enabled. Confidential clients (client authentication on, with a secret) and public clients (PKCE, no secret) both work.

Set the URLs. Valid redirect URI https://<dashboard-host>/api/auth/oidc/callback; valid post-logout redirect URI https://<dashboard-host>/sign-in; web origin https://<dashboard-host>.

Scopes. openid profile email. Keep email โ€” it is what names the operator in audit entries.

Add an audience mapper. Keycloak mints aud: account alongside your client id, so add an Audience protocol mapper that includes the client id. Sentisec's audience setting accepts a comma-separated list and matches on any entry, so configure it to accept both.

2 ยท The workspace claim

Workspace membership rides on one claim, sentisec_workspaces by default. Add a protocol mapper that emits it into both the ID token and the access token โ€” a User Attribute mapper with a JSON claim type is the usual way to carry a per-user map.

Four shapes are accepted and all normalise to the same workspace-to-role mapping:

Claim valueReading
{"<workspace-id>": "admin"}object: id โ†’ role
[{"id": "<workspace-id>", "role": "admin"}]array of objects
["<workspace-id>", "<workspace-id>:admin"]array of strings, optional :role
"<workspace-id>:admin <workspace-id-2>"one space- or comma-separated string

Rules worth knowing before you write the mapper:

  • A bare id with no role means member. Least privilege is the default everywhere; an omitted role is never read as admin.
  • Role spellings match case-insensitively. admin, owner, and the org:admin / workspace:admin prefixed forms all mean admin. Anything else means member.
  • Dotted claim names work. Set the claim name to realm_access.sentisec_workspaces if your mapper nests the value, which Keycloak realm mappers commonly do.
  • An unparseable claim yields no roles at all, which denies. It never falls back to an admit.
Info
Prefer the object or array-of-objects shape

The API understands all four, but the dashboard's workspace picker reads the first three. With a bare "<id>:admin" string entry the interface will show you as a member while the API still enforces admin. The object and array-of-objects forms agree exactly.

The admin claim

sentisec_admin is a global elevation switch. A truthy value โ€” true, 1, yes, on, or a list containing admin โ€” makes the subject an admin of every workspace their workspace claim already names.

It never grants membership on its own. A global admin whose workspace claim is empty sees nothing. Elevation and membership are separate decisions, so a mis-mapped boolean cannot hand a stranger the whole install.

Both claim names are configurable if sentisec_workspaces and sentisec_admin collide with something you already emit.

3 ยท Configure the install

Both halves โ€” the API and the dashboard โ€” need pointing at your realm: the issuer, the client id and secret, the audience to accept, the claim names above, and a session secret for the dashboard's cookie. The configuration reference ships with your install package; request access if you do not have it.

Three behaviours are worth knowing up front, because each has bitten someone:

  • The mode is all-or-nothing. OIDC settings stay inert until the install is switched into OIDC mode, so a half-finished configuration cannot half-apply.
  • The dashboard image must be built for the mode it runs. The browser half reads the auth mode from a value fixed at image build time, so setting it only at runtime is not enough. An image built for one mode and run in the other logs a loud incoherence warning at startup rather than failing quietly.
  • Do not leave hosted-identity configuration in place. If the API still sees hosted-identity settings, it installs a verification override at startup that shadows the OIDC path, and sign-in fails with nothing obviously wrong in your OIDC configuration. Clear them, and check the startup logs first if logins fail โ€” the warning is there.

The issuer is derived automatically from a Keycloak-shaped keys URL. For any other layout, set it explicitly: if the issuer can be neither read nor derived, OIDC stays off rather than verifying against an unpinned issuer.

4 ยท First sign-in

There is no user directory to populate. Workspaces are created by your operator at install time, and a person becomes a member of the workspaces their token names on their first verified sign-in.

Each first-time pairing of workspace and subject writes one workspace.member_added audit entry marked via: oidc_jit. Repeat sign-ins are idempotent and write nothing further. The behaviour can be switched off if you would rather provision membership yourself.

Provisioning runs after the verification decision and is best-effort: a failed write is logged, and never turns a valid sign-in into a rejection nor an invalid one into an admission.

5 ยท Verify

Discovery reaches the realm. From the dashboard host, https://<keycloak-host>/realms/<realm>/.well-known/openid-configuration must resolve and return metadata.

Sign in. The dashboard redirects to Keycloak, and Keycloak returns you to /api/auth/oidc/callback and on into the app.

Check the workspace picker. It should list exactly the workspaces your claim names โ€” no more, no fewer.

Check the roles. An admin-only action succeeds for a subject mapped to admin and is refused for one mapped to member. The control plane is the enforcement point; the interface only predicts it.

Check the audit trail. The workspace audit log carries a workspace.member_added entry for each first sign-in. See Audit and metrics.

What this mode does not cover

Real constraints, not configuration mistakes:

  • Workspace lifecycle is operator-managed. Creating and deleting workspaces is not exposed in the interface in this mode; workspaces come from your install-time configuration.
  • Per-user interface preferences do not persist. Saved views, the time-range selection, and guided-tour state fall back to defaults every session. The dashboard warns once rather than failing.
  • Hosted-plan surfaces are off. The billing portal, organisation invitations, and the SCIM path described for hosted tenants do not apply here.
  • Authorization code flow only. No SAML and no IdP-initiated sign-in. Front-channel logout is wired, with a local-only fallback when your provider publishes no end-session endpoint; back-channel logout is not available.

If one of these blocks your deployment, tell us โ€” knowing which one matters changes what we build next.

Protected docs