> ## Documentation Index
> Fetch the complete documentation index at: https://docs.corepillar.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# SSO with Okta

> Connect an Okta OIDC application, map Okta groups to roles, enforce single sign-on, and understand just-in-time provisioning.

"Configure Okta OIDC for this organization. Client secrets are encrypted at rest and never shown again after saving."

With SSO configured, members of your organization can sign in through Okta instead of an emailed code. AgentHub uses the OpenID Connect Authorization Code flow with PKCE, and creates accounts and memberships on first sign-in (just-in-time provisioning).

Each organization has at most one Okta configuration.

## Who can configure it

The **SSO** tab is shown only to super admins. Saving also requires the `manage_sso` permission, which super admins always have. Org Owners and Admins hold `manage_sso` too but do not see the tab, so ask one of your platform's super admins to set SSO up.

## Before you start: create the Okta app

In the Okta admin console:

<Steps>
  <Step title="Create an OIDC web application">
    Create an app integration with sign-in method **OIDC - OpenID Connect** and application type **Web Application**. Use the **Authorization Code** grant and client authentication with a **client secret**.
  </Step>

  <Step title="Add the sign-in redirect URI">
    Copy it from the bottom of the SSO tab (**Copy redirect URI**). It is built from the address you are using, so for AgentHub it is:

    ```text theme={null}
    https://pillarforge.corepillar.ai/auth/sso/okta/callback
    ```

    If your people also sign in to ModelHub through SSO, add ModelHub's callback as a second redirect URI:

    ```text theme={null}
    https://modelhub.corepillar.ai/auth/sso/okta/callback
    ```
  </Step>

  <Step title="Send groups in the ID token">
    To map Okta groups to roles, configure a `groups` claim on the ID token. AgentHub requests the `groups` scope only when your authorization server advertises it, and reads groups from the ID token.
  </Step>

  <Step title="Assign people">
    Assign the users or groups who should sign in to the app. People who are not assigned are turned away by Okta.
  </Step>
</Steps>

AgentHub requests the scopes `openid`, `profile`, and `email`, plus `groups` when supported. The ID token must include an email; if it does not, AgentHub fetches it from Okta's userinfo endpoint.

## Fields

### Okta application

"Credentials from the OIDC web app you created in the Okta admin console."

| Field | Required | Notes |
| - | - | - |
| **Okta domain** | Yes | Placeholder `https://your-company.okta.com`. You can paste `your-company.okta.com`, a full URL with a trailing slash, a custom authorization server issuer such as `https://your-company.okta.com/oauth2/default`, or even an admin console URL (`your-company-admin.okta.com/...`); AgentHub reduces it to the issuer. |
| **Client ID** | Yes | From the Okta app. |
| **Client secret** | First time only | A password field. On later saves the placeholder reads "Leave blank to keep existing secret" and the hint says "A client secret is already saved. Enter a new value only to rotate it." The secret is encrypted with AES-256-GCM and never returned. |

When the domain has no `/oauth2/...` path, AgentHub uses your Okta org authorization server, and falls back to `/oauth2/default` if that does not answer.

### Role provisioning

"How members are assigned a role the first time they sign in through Okta."

| Field | Notes |
| - | - |
| **Default role (JIT provisioning)** | **Admin**, **Member (Editor)**, or **Viewer**. Defaults to Member (Editor). Used when no group mapping applies. |
| **Group → role mapping (JSON)** | A JSON object from Okta group name to role. Values are `admin`, `member`, or `viewer`. Leave it `{}` to use only the default role. |

Example mapping:

```json theme={null}
{
  "Okta-Group-Admins": "admin",
  "Okta-Group-Eng": "member",
  "All-Employees": "viewer"
}
```

If a person is in several mapped groups, the highest role wins: admin, then member, then viewer. Group names must match exactly.

| Mapping value | Organization role | Hub role |
| - | - | - |
| `admin` | Admin | Admin |
| `member` | Editor | AI Builder |
| `viewer` | Viewer | User |

### Enforce SSO

A checkbox: **Enforce SSO**, "Disable email/password login for members of this organization."

Ticking it and saving shows an inline confirmation: "Enforce SSO? Members will no longer be able to sign in with email and password for this organization." Select **Enforce and save** or **Cancel**.

You cannot enforce SSO unless the organization has at least one Owner or Admin who already signs in through Okta, or you are an Owner or Admin yourself. Otherwise the save fails with "Cannot enforce SSO without at least one admin who can sign in via Okta".

Once enforced, anyone who is a member of the organization, or has a pending invite to it, can no longer request an email code. The sign-in page hides the code button and says "Your organization requires single sign-on." This applies to that email everywhere, including other organizations they belong to.

### Saving

Select **Save SSO configuration**. The footer shows "Last updated `<date and time>`" or "Not configured yet." Changes are recorded in the organization's audit log.

| Error | Fix |
| - | - |
| Okta domain and client ID are required. | Fill both in. |
| Client secret is required for new SSO configuration. | The first save needs a secret. |
| Invalid default role. | Pick one of the three options. |
| Group role mapping must be a JSON object. | Use `{ ... }`, not a list or a string. Invalid JSON shows the parser's error. |
| Cannot enforce SSO without at least one admin who can sign in via Okta | See [Enforce SSO](#enforce-sso). |

## How people sign in

```mermaid theme={null}
sequenceDiagram
  participant U as Person
  participant A as AgentHub
  participant O as Okta
  U->>A: Types email on sign-in page
  A-->>U: Shows "Sign in with SSO" if the email matches an SSO org
  U->>A: Selects Sign in with SSO
  A->>O: Redirect with PKCE, state, login hint
  O-->>U: Okta sign-in
  O->>A: Redirect to /auth/sso/okta/callback with code
  A->>O: Exchange code for tokens
  A->>A: Provision profile and membership
  A-->>U: Signed in, sent to original page
```

### SSO discovery

The SSO button appears only when the typed email is already a **member** of an organization with SSO configured, or has a **pending, unexpired invite** to one. AgentHub does not match by email domain. If the email matches several such organizations, one button per organization appears, labelled **SSO: `<org name>`**. The check runs about 400 ms after you stop typing.

<Note>
  Because discovery needs a membership or an invite, a brand-new colleague who has neither will not see the SSO button. Invite them first, or give them the direct link `https://pillarforge.corepillar.ai/auth/sso/okta/login?org_slug=<your-org-slug>`.
</Note>

The discovery lookup is rate-limited to 30 requests per minute per client address.

### Just-in-time provisioning

On a successful Okta sign-in, AgentHub:

1. **Resolves a role.** The highest mapped Okta group wins. With no matching group, a pending invite's role is used, and otherwise the default role.
2. **Finds or creates the account.** It looks for a profile linked to the Okta user's subject ID, then for one with the same email (and links it), and otherwise creates a new, already-confirmed account.
3. **Creates or updates the membership.**
   * A new member gets the resolved role, and any pending invite for that email is marked accepted.
   * An existing member's role changes **only** if one of their Okta groups is mapped. The default role never demotes an existing Owner or Admin.
4. **Starts a session** and sends them to the page they originally asked for, or the home page.

Okta sign-ins are not checked against [Platform access](/agenthub/settings/platform-access). Whether someone can sign in through SSO is controlled by their assignment to the Okta app.

## Troubleshooting

| Sign-in page message | Likely cause |
| - | - |
| Okta denied access. Ask your admin to assign you to the CorePillar app in Okta. | The person is not assigned to the Okta application. |
| Couldn't start Okta sign-in. Check your SSO configuration. | AgentHub could not discover the issuer. Check **Okta domain**, and that the org or default authorization server is reachable. |
| Couldn't sign in with Okta. Contact your admin. | The token exchange or provisioning failed. Common causes: the redirect URI in Okta does not exactly match the one on the SSO tab, a wrong client secret, or the ID token has no email. |
| SSO session expired. Try signing in again. | More than 10 minutes passed between leaving for Okta and returning, or the browser dropped cookies. |
| SSO is not configured for that organization. | No configuration is saved for the organization in the link. |
| Organization not found. / Enter your organization slug to use Okta SSO. | The `org_slug` in a direct link is wrong or missing. |

<AccordionGroup>
  <Accordion title="Everyone lands as the default role, groups are ignored">
    Groups are not reaching AgentHub. Add a `groups` claim to the ID token in Okta, make sure the authorization server supports the `groups` scope, and check that the names in your mapping match the Okta group names exactly.
  </Accordion>

  <Accordion title="An existing admin was not demoted after removing their Okta group">
    Roles of existing members only change when a mapped group applies. Losing a group does not lower a role. Change it in [User Management](/agenthub/settings/user-management).
  </Accordion>

  <Accordion title="People can't get a sign-in code any more">
    SSO is enforced for an organization they belong to or are invited to. They must use the SSO button.
  </Accordion>
</AccordionGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.