> ## 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.

# Quick Start: Sign in with Cativa in 5 minutes

> Implement OAuth 2.0 with PKCE and let Cativa users log into your app.

In 5 minutes you'll have a **Sign in with Cativa** button working in your app. This flow is ideal when you want the end user to authenticate with their Cativa community account.

<Note>
  Cativa SSO endpoints follow the OIDC standard and are organized by **tenant slug** (`{customerName}`). That slug is the community's public subdomain — confirm with the tenant admin which value to use.
</Note>

<Steps>
  <Step title="Create an OAuth App in the Console">
    Go to [app.cativa.digital/admin/developers](https://app.cativa.digital/admin/developers), **OAuth Apps** tab, click **Create app**.

    Save the returned `client_id` and `client_secret`. **The secret is shown only once** — store it carefully.
  </Step>

  <Step title="Configure the redirect URI">
    In the same modal, add your redirect URI (e.g. `https://myapp.com/callback` or `http://localhost:3000/callback` for development).
  </Step>

  <Step title="Redirect the user to /authorize">
    On the frontend, generate a `code_verifier` and `code_challenge` (PKCE), then redirect to the tenant's `/authorize` endpoint:

    ```js theme={null}
    const verifier = generateRandomString(64);
    const challenge = await sha256(verifier);
    sessionStorage.setItem('pkce_verifier', verifier);

    const params = new URLSearchParams({
      client_id: 'YOUR_CLIENT_ID',
      redirect_uri: 'https://myapp.com/callback',
      response_type: 'code',
      scope: 'openid profile email',
      code_challenge: challenge,
      code_challenge_method: 'S256',
      state: crypto.randomUUID()
    });

    // Replace {customerName} with the tenant slug
    window.location.href = `https://apis.cativalab.digital/tenant/api/v2/sso/{customerName}/authorize?${params}`;
    ```
  </Step>

  <Step title="Exchange the code for an access_token in the callback">
    After the user consents, Cativa redirects to your URL with `?code=...&state=...`. On the backend, `POST` to the same tenant's `/token` endpoint with the body in `application/x-www-form-urlencoded`:

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://apis.cativalab.digital/tenant/api/v2/sso/{customerName}/token \
        -H "Content-Type: application/x-www-form-urlencoded" \
        -d "grant_type=authorization_code" \
        -d "code=CODE_FROM_CALLBACK" \
        -d "client_id=YOUR_CLIENT_ID" \
        -d "client_secret=YOUR_SECRET" \
        -d "redirect_uri=https://myapp.com/callback" \
        -d "code_verifier=VERIFIER_FROM_SESSION"
      ```

      ```js Node theme={null}
      const res = await fetch(`https://apis.cativalab.digital/tenant/api/v2/sso/${customerName}/token`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: new URLSearchParams({
          grant_type: 'authorization_code',
          code: req.query.code,
          client_id: process.env.CATIVA_CLIENT_ID,
          client_secret: process.env.CATIVA_CLIENT_SECRET,
          redirect_uri: 'https://myapp.com/callback',
          code_verifier: req.session.pkceVerifier
        })
      });
      const { access_token, id_token } = await res.json();
      ```

      ```python Python theme={null}
      import requests
      res = requests.post(f'https://apis.cativalab.digital/tenant/api/v2/sso/{customer_name}/token', data={
          'grant_type': 'authorization_code',
          'code': code,
          'client_id': os.environ['CATIVA_CLIENT_ID'],
          'client_secret': os.environ['CATIVA_CLIENT_SECRET'],
          'redirect_uri': 'https://myapp.com/callback',
          'code_verifier': session['pkce_verifier']
      })
      tokens = res.json()
      ```
    </CodeGroup>

    The response follows the OIDC standard and contains `access_token`, `token_type`, `expires_in` and `id_token`.
  </Step>

  <Step title="Fetch the user's info">
    Use the `access_token` against the userinfo endpoint:

    ```bash theme={null}
    curl https://apis.cativalab.digital/tenant/api/v2/sso/{customerName}/userinfo \
      -H "Authorization: Bearer ACCESS_TOKEN"
    ```

    Response:

    ```json theme={null}
    {
      "sub": "01HQ7Z3X4Y5Z6A7B8C9D0E1F2G",
      "name": "Mary Smith",
      "email": "mary@example.com",
      "picture": "https://cdn.cativa.digital/avatars/..."
    }
    ```
  </Step>
</Steps>

<Note>
  The tenant's OIDC discovery document lives at `https://apis.cativalab.digital/tenant/api/v2/sso/{customerName}/.well-known/openid-configuration` and lists every endpoint (`authorize`, `token`, `userinfo`, `jwks`) plus the supported algorithms (`S256` for PKCE, `ES256` for `id_token` signing). The public JWKS is served at `https://apis.cativalab.digital/tenant/api/v2/sso/{customerName}/jwks`. Libraries like [jose](https://www.npmjs.com/package/jose) (Node) or [PyJWT](https://pyjwt.readthedocs.io/) (Python) read the discovery doc and validate the `id_token` automatically.
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Tenants and Customers" icon="building" href="/en/concepts/tenants-and-customers">
    Understand the `customerName` concept in the OIDC flow and when tenant matters in integrations.
  </Card>

  <Card title="First API call" icon="terminal" href="/en/get-started/quickstart-api-key">
    For server-to-server integrations, use an API Key directly instead of OAuth.
  </Card>
</CardGroup>
