Connecting your identity provider
Clausgap needs to know two kinds of identity:
- People — admins, policy authors, reviewers and approvers. They sign in to the dashboard through your organisation's identity provider (IdP) with single sign-on.
- Agents — every request an agent sends through the proxy must say which agent it is, in a way the proxy can verify. The same IdP can issue agents signed tokens; API keys and signed SDK requests work too.
You can try every step below — with a test identity provider standing in for yours — in the evaluation kit, which also checks each step end to end.
People: single sign-on
1. Register Clausgap with your IdP
In Okta, Entra ID, Google Workspace, Auth0, Keycloak or any OIDC provider, create a web application (OIDC, authorization code flow):
| Setting | Value |
|---|---|
| Redirect / callback URL | https://<your dashboard>/api/auth/sso/callback |
| Scopes | openid email profile |
| Assigned users | Everyone who should use Clausgap |
Note the issuer URL, the client ID and the client secret.
2. Connect it and verify your domain
Sign in as an admin, go to Settings → SSO and enter the issuer, client ID and secret. Clausgap checks the issuer straight away by fetching its discovery document, so a wrong issuer fails here, not at someone's first sign-in.
Then add your email domain — for example example.com — and prove you own it. Clausgap shows a DNS TXT record to publish:
| Name | Value |
|---|---|
| _clausgap-verification.example.com | clausgap-domain-verification=<token> |
Publish it with your DNS provider, then select Verify. Until a domain is verified, no sign-in is routed through it. Only one organisation can verify a given domain, so nobody can claim your domain and send your people to their identity provider. You can remove the record after verification.
The API equivalent, with an admin session:
PUT /v1/settings/sso {"issuer_url": "...", "client_id": "...", "client_secret": "..."}
POST /v1/settings/sso/domains {"domain": "example.com"} → domain_records[].txt_name / txt_value
POST /v1/settings/sso/domains/example.com/verifyFrom then on, anyone who enters an address in that domain on the sign-in page is sent to your IdP.
3. Give people roles
Roles live in Clausgap:
| Role | Can |
|---|---|
| viewer | See everything; change nothing |
| author | Create and edit policies, and submit them for review |
| approver | Approve or reject policy changes and held agent actions |
| admin | Everything, including SSO, team, settings and break-glass |
Invite people from Team, with their role. When they first sign in through the IdP, they have the invited role — the invite and the SSO sign-in are the same account, matched by email. Someone who signs in through SSO without an invite gets an account with no roles, and can't change anything until an admin grants one.
Clausgap only accepts an SSO sign-in when both of these hold:
- the email is in one of your verified domains;
- your IdP hasn't marked the email as unverified (
email_verified: false).
4. People leaving
Removing someone in Team ends their sessions at once and takes away their roles. They then can't sign in again by any route — SSO or emailed link — until an admin invites them again, which restores the same account. Disable them in your IdP as well, as usual.
Requiring a second person for policy changes
Turn on Settings → Require approval. From then on, a policy change only takes effect once someone other than its author approves that exact version — whether it came from the dashboard, the API or a CI pipeline. Until then, proxies keep enforcing the last approved version. Break-glass (admins only, with a reason) deploys a change immediately and is recorded at critical severity; turning approval off is recorded too.
Agents: identity the proxy can verify
An agent's identity decides which policy applies to it, so it must come from something the agent can't forge. Clausgap never takes it from plain request headers — X-Agent-Role: admin from a caller changes nothing. Choose one of these per agent.
Tokens from your IdP (JWT)
Use this when your agents already get tokens from your IdP — client credentials, workload identity, or a service account. Register the IdP as a token issuer, with the tenant's API key or an admin session:
// POST /v1/keyless/jwt-providers
{
"name": "Corporate IdP",
"issuer": "https://login.example.com/",
"jwks_uri": "https://login.example.com/.well-known/jwks.json",
"audience": "clausgap",
"allowed_algorithms": ["RS256"],
"claims_mapping": { "agent_role": "clausgap_role", "agent_id": "sub" }
}The agent sends Authorization: Bearer <token>. The proxy checks the signature against the IdP's published keys (JWKS), the issuer, the audience and the expiry; claims_mapping names the claim that carries the agent's role. The proxy rejects a token with an edited claim, an unsigned token (alg: none), a token issued to a different application (wrong audience), and any algorithm you didn't allow.
API keys
Issue an agent a key with a role and, optionally, the networks it may be used from:
// POST /v1/keyless/api-keys
{
"name": "reporting agent",
"key_value": "<a long random secret you give the agent>",
"agent_role": "reporting_agent",
"agent_id": "reporting-1",
"allowed_cidrs": ["10.20.0.0/16"]
}The agent sends the key in the header you configure, for example X-Agent-Key. Revoking it (DELETE /v1/keyless/api-keys/{id}) takes effect when the proxy next refreshes its credentials — every keyless.poll_interval, 30 seconds by default.
The same key in direct mode. An agent using a framework integration can use its agent key instead of the tenant API key: give the SDK the key and set CLAUSGAP_TENANT_ID (sent as X-Clausgap-Tenant). The key decides who the agent is — a request naming another role or agent is refused (403 IDENTITY_MISMATCH) — and it can only evaluate tool calls and read its own holds, never create policies or issue keys. Its allowed_cidrs, expiry and revocation apply, and Discovery Mode lets a direct-mode call through only with an agent key. A tenant API key still works in direct mode, but lets the agent name any role and can do everything the tenant can: keep those for pipelines and the proxy.
Signed SDK requests
The Python and TypeScript SDKs sign each request's identity with a secret shared with the proxy (hmac_secret on the HTTP listener). The signature covers the method, the path, the body and a timestamp, so a captured request can't be replayed or altered. Give each agent its own secret bound to its role under signing_keys on the HTTP or MCP listener: a header signed with it must claim that role. The single shared hmac_secret still works, but anyone holding it can sign as any role, and Discovery Mode never lets its callers through. See Direct HTTP & SDK.
Behind a load balancer
If the proxy or control plane sits behind a load balancer, list it in trusted_proxies so client addresses come from the forwarded header it adds. Without this, per-IP rate limits and allowed_cidrs see the load balancer's address; with it, a client can't set its own address, because only hops you trust are believed.
trusted_proxies: ["10.0.0.0/8"]
client_ip_header: "X-Forwarded-For"Known limit: IdP groups don't map to Clausgap roles yet — roles are given by invite or by an admin in Clausgap.