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

# Federated login (your own IdP)

> Let members sign into a Cativa community with the company's own identity provider (Google Workspace, Microsoft Entra, Okta, Keycloak or a custom OIDC IdP). Here Cativa is the Relying Party.

This guide is the **inverse** of [Sign in with Cativa](/en/guides/sign-in-with-cativa).

* In *Sign in with Cativa*, **Cativa is the IdP**: a third-party app authenticates the user with the community account.
* Here, the **company is the IdP**: members sign into the Cativa community using the login system the company already has. Cativa acts as the **Relying Party** and federates to the client's IdP.

Use this when the community belongs to an organization that already has an identity directory (Google Workspace, Microsoft Entra/Azure AD, Okta, Auth0, Keycloak, or any OIDC provider) and members shouldn't create a separate password.

<Info>
  Want to test without a real IdP? The [`cativa-sso`](https://github.com/cativalab/cativa-sso) repo ships a runnable **mock** OIDC IdP in the `cliente-idp` folder (Node + Express): discovery, RS256 JWKS, `authorize`/`token`/`userinfo`. Run it behind a public tunnel and register it as a provider to exercise the flow end to end.
</Info>

## How it works

```mermaid theme={null}
sequenceDiagram
    autonumber
    actor U as Member (browser)
    participant Cativa as Cativa (Relying Party)
    participant IdP as Company IdP
    participant FE as Community frontend

    U->>Cativa: "Sign in with your company account"<br/>/sso/external/{customer}/{slug}/authorize
    Cativa->>IdP: 302 /authorize<br/>client_id, redirect_uri, state
    U->>IdP: Authenticates (company login)
    IdP-->>Cativa: 302 /sso/external/{customer}/callback?code=&state=
    Cativa->>IdP: POST /token (exchange the code)
    IdP-->>Cativa: id_token
    Cativa->>IdP: GET /jwks (verify signature)
    Note over Cativa: validate state, extract email,<br/>link or auto-provision the user
    Cativa->>FE: 302 already signed in
```

Cativa verifies the `id_token` signature (via the provider's JWKS), checks the `audience` (your `client_id`) and requires the `email` claim. On first login, if **auto-provisioning** is on, the user is created in the community from the IdP data.

## Prerequisites

1. **An OAuth/OIDC app in your IdP** — create a client in the company provider and note its `client_id` and `client_secret`.
2. **Cativa's callback URL, registered in your IdP** as an allowed redirect URI. You don't have to build it by hand: when you open the Identity Provider form in the Cativa admin (next section), it shows the **exact callback URL with your slug already filled in**, plus a **Copy** button. Paste that value into your IdP client. The shape is:

   ```
   https://apis.cativalab.digital/tenant/api/v2/sso/external/{customerName}/callback
   ```

   `{customerName}` is your community's slug — the public Cativa subdomain (`https://{customerName}.cativa.digital`). It's fixed per tenant, and the admin who sets this up already knows it (it's their own community). If in doubt, copy the URL the form shows rather than typing the slug yourself.
3. **Admin access** to the Cativa community to register the provider.

## Register the Identity Provider

In the community admin, under **Integrations → SSO → Providers → New provider**, pick your IdP preset (Google, Microsoft, Okta, Auth0, Keycloak) or **Custom (OIDC)** and fill in:

<Steps>
  <Step title="Discovery">
    Paste the **Metadata URL** (your IdP's `.well-known/openid-configuration`) and click **Discover endpoints** — Cativa fills `authorize`/`token`/`userinfo`/`jwks` for you. (For providers without discovery, enter the endpoints manually.)
  </Step>

  <Step title="Credentials">
    Enter the `Client ID` and `Client Secret` of the client you created in the IdP, and the `Scopes` (at minimum `openid email profile`).
  </Step>

  <Step title="Provisioning">
    Turn on **Auto-provision** to create the user on first login, and choose the **Default role** (User or Admin). Without auto-provisioning, only already-existing (and linked) users can sign in.
  </Step>

  <Step title="Slug">
    Set a **slug** (e.g. `company`) — it goes in the federated login start URL.
  </Step>
</Steps>

<Note>
  Keep **only one** provider enabled while testing. The callback identifies the provider via a cookie set at the start; with multiple providers enabled and the cookie missing, identification is ambiguous.
</Note>

## Trigger the login

Point your "Sign in with your company account" button to:

```
https://apis.cativalab.digital/tenant/api/v2/sso/external/{customerName}/{slug}/authorize
```

Cativa redirects to the company IdP, receives the `code` at the callback, validates the `id_token`, links/auto-provisions the user, and redirects the browser back to the community **already authenticated**.

## What the IdP must return

* A signed `id_token`, verifiable against the provider's `jwks` (Cativa fetches the public key from discovery/JWKS).
* The **`email`** claim is required — it's the identity key. Without it, login is rejected.
* `given_name` / `family_name` / `picture` are used (when present) when auto-provisioning the user.

## Common errors

<AccordionGroup>
  <Accordion title="`Invalid or expired state` at the callback">
    The anti-CSRF cookie (`sso_state`) didn't come back. Make sure the IdP redirected to the **exact** registered callback URL (`.../tenant/api/v2/sso/external/{customerName}/callback`, with the full prefix) and the flow completed within 10 minutes.
  </Accordion>

  <Accordion title="`The provider did not return a valid email`">
    The `id_token` came without the `email` claim. Adjust the scopes/claims on your IdP client to include `email` (and `email_verified` where applicable).
  </Accordion>

  <Accordion title="`Identity provider could not be determined`">
    More than one provider is enabled and the identification cookie was lost. Keep only one enabled, or always start from the `/sso/external/{customerName}/{slug}/authorize` URL (with the right `slug`).
  </Accordion>

  <Accordion title="`Invalid identity token`">
    The `id_token` signature didn't validate, or the `audience` doesn't match. Confirm the registered `Client ID` equals the `aud` your IdP issues, and that the Metadata URL points to the right JWKS.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Sign in with Cativa" icon="arrow-right-to-bracket" href="/en/guides/sign-in-with-cativa">
    The inverse flow: Cativa as the IdP for third-party apps.
  </Card>

  <Card title="Example IdP (GitHub)" icon="github" href="https://github.com/cativalab/cativa-sso">
    The `cliente-idp` folder is a runnable mock OIDC IdP to test this flow.
  </Card>
</CardGroup>
