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

# Single Sign-On (SSO)

> Configure OIDC SSO for your Enterprise team

## Overview

Single Sign-On (SSO) allows your team members to authenticate through your organization's Identity Provider (IdP) instead of using individual Google or magic link sign-in methods. This gives your IT team centralized control over who can access Qwairy, automatic onboarding for new employees, and a single point of revocation when someone leaves.

Qwairy supports **OIDC (OpenID Connect)** with **Authorization Code + PKCE** flow, the most secure and widely supported SSO protocol.

<Note>
  SSO is available on the **Enterprise plan** only. [See pricing](https://qwairy.co/pricing) or [book a demo](https://qwairy.co/demo).
</Note>

## Supported Identity Providers

Qwairy works with any OIDC-compliant Identity Provider, including:

<CardGroup cols={3}>
  <Card title="Okta" icon="shield-check">
    Full OIDC support with automatic discovery
  </Card>

  <Card title="Azure AD" icon="microsoft">
    Microsoft Entra ID (formerly Azure Active Directory)
  </Card>

  <Card title="Google Workspace" icon="google">
    Google Cloud Identity / Workspace
  </Card>
</CardGroup>

Other OIDC-compliant providers (OneLogin, Auth0, Ping Identity, JumpCloud, etc.) are also supported.

## Setup Guide

<Steps>
  ### Create an OIDC application in your Identity Provider

  You need to register Qwairy as an OIDC application in your IdP. The exact steps vary by provider:

  <Note>
    **Redirect URI:** the examples below use `https://qwairy.co/api/auth/sso/callback`. If your team signs in on a white-label [custom domain](/white-label/custom-domain/overview), use **your** domain instead, e.g. `https://analytics.yourcompany.com/api/auth/sso/callback`. The path is always `/api/auth/sso/callback`. See [SSO on a custom domain](#sso-on-a-custom-domain) below.
  </Note>

  <Tabs>
    <Tab title="Okta">
      1. Go to **Applications > Create App Integration**
      2. Select **OIDC - OpenID Connect** and **Web Application**
      3. Set the **Sign-in redirect URI** to: `https://qwairy.co/api/auth/sso/callback`
      4. Set **Sign-out redirect URI** to: `https://qwairy.co`
      5. Under **Assignments**, assign the app to the relevant groups
      6. Copy the **Client ID** and **Client Secret**
      7. Your Issuer URL is: `https://your-org.okta.com`
    </Tab>

    <Tab title="Azure AD">
      1. Go to **Azure Portal > Microsoft Entra ID > App registrations > New registration**
      2. Set **Redirect URI** (Web) to: `https://qwairy.co/api/auth/sso/callback`
      3. Under **Certificates & secrets**, create a new **Client secret** and copy its value
      4. Copy the **Application (client) ID** from the Overview page
      5. Your Issuer URL is: `https://login.microsoftonline.com/{tenant-id}/v2.0`
    </Tab>

    <Tab title="Google Workspace">
      1. Go to **Google Cloud Console > APIs & Services > Credentials > Create OAuth client ID**
      2. Select **Web application**
      3. Add **Authorized redirect URI**: `https://qwairy.co/api/auth/sso/callback`
      4. Copy the **Client ID** and **Client Secret**
      5. Your Issuer URL is: `https://accounts.google.com`
    </Tab>
  </Tabs>

  ### Configure the connection in Qwairy

  1. Go to **Team Management > SSO** in your Qwairy dashboard
  2. Enter the **Issuer URL**, **Client ID**, and **Client Secret** from your IdP
  3. Optionally set a **Provider Name** (e.g., "Okta") — this is shown to users on the login page
  4. Select the **Default Role** for auto-provisioned users (Member or Viewer)
  5. Click **Create SSO Connection**

  ### Add and verify your domain

  1. In the Domains section, click **Add Domain**
  2. Enter your company domain (e.g., `acme.com`)
  3. You'll receive a DNS TXT record to add to your domain:

  ```
  Type: TXT
  Host: acme.com
  Value: qwairy-verify=<your-unique-token>
  ```

  4. Add this record in your DNS provider (Cloudflare, Route 53, GoDaddy, etc.)
  5. Wait for DNS propagation (usually minutes, can take up to 48 hours)
  6. Click **Verify** next to the domain

  ### Test the connection

  Click **Test OIDC Connection** to verify that Qwairy can reach your IdP's discovery endpoint and JWKS. This validates the issuer URL without performing a full login.

  ### Enable SSO

  Once you have at least one verified domain, toggle **Enable SSO** to activate it. Users with email addresses matching your verified domains will be redirected to your IdP when signing in.
</Steps>

## Domain Verification

Domain verification proves that your organization owns the email domain you want to use for SSO. This prevents unauthorized teams from claiming your domain.

When you add a domain, Qwairy generates a unique verification token. You add this as a DNS TXT record:

```
qwairy-verify=a1b2c3d4e5f6...
```

Qwairy checks for this record when you click Verify. Once confirmed, the domain is permanently linked to your team's SSO connection.

<Info>
  Each domain can only be linked to one team. If you see "domain already claimed", contact support.
</Info>

## JIT Provisioning

**Just-In-Time (JIT) provisioning** automatically creates user accounts when team members sign in via SSO for the first time.

When a user authenticates through your IdP:

1. Qwairy receives their email, name, and profile picture from the OIDC claims
2. If the user doesn't exist, a new account is created automatically
3. The user is added to your team with the **Default Role** you configured (Member or Viewer)
4. If the user already exists (e.g., from a previous invitation), they are linked to your SSO connection

No manual user creation or invitation is required.

## SSO on a Custom Domain

If your team uses a white-label [custom domain](/white-label/custom-domain/overview) (e.g. `analytics.yourcompany.com`), your users sign in on that domain, so the SSO callback runs there too. Register the redirect URI for **your** domain in your IdP:

```
https://analytics.yourcompany.com/api/auth/sso/callback
```

Qwairy automatically uses your team's custom domain for the SSO callback and the post-login redirect, so the entire sign-in flow stays on your brand — your users never see the Qwairy domain. No extra Qwairy configuration is needed beyond having the custom domain set up; just make sure the redirect URI registered at your IdP matches your domain.

<Note>
  Teams without a custom domain use `https://qwairy.co/api/auth/sso/callback`. You can register both your custom domain and the Qwairy URI in your IdP if you sign in from both.
</Note>

## Enforce SSO

When **Enforce SSO** is enabled, team members with email addresses matching your verified domains are **required** to sign in through your IdP. Google sign-in and magic links are blocked for these users.

<Warning>
  Before enabling enforcement, make sure all team members can successfully sign in via SSO. Platform administrators (Qwairy admins) bypass enforcement as a safety measure.
</Warning>

To enable enforcement:

1. Ensure SSO is enabled and working
2. Toggle **Enforce SSO** in Team Management > SSO settings
3. Non-SSO sign-in attempts from your domain will be redirected to the SSO login

## Troubleshooting

<AccordionGroup>
  <Accordion title="Discovery failed">
    * Verify the **Issuer URL** is correct and accessible
    * Ensure the URL points to the OIDC provider root (e.g., `https://accounts.google.com`, not `https://accounts.google.com/.well-known/openid-configuration`)
    * Check that your IdP is not blocking external requests
  </Accordion>

  <Accordion title="Invalid redirect URI / redirect_uri_mismatch">
    * Ensure your IdP has the redirect URI for **your** Qwairy domain: `https://qwairy.co/api/auth/sso/callback`, or `https://your-custom-domain/api/auth/sso/callback` if you use a white-label custom domain
    * The URL must match exactly, including the protocol (`https`) and the `/api/auth/sso/callback` path
  </Accordion>

  <Accordion title="Domain not verified">
    * Verify the DNS TXT record is set on the correct domain
    * DNS propagation can take up to 48 hours — try again later
    * Use a DNS lookup tool to confirm the TXT record is visible
  </Accordion>

  <Accordion title="User not provisioned">
    * Ensure the user's email domain matches a verified SSO domain
    * Check that the IdP is returning the `email` claim in the `id_token`
    * Verify the `openid email profile` scopes are configured in your IdP
  </Accordion>
</AccordionGroup>

## Security

Qwairy's SSO implementation follows security best practices:

| Measure                      | Detail                                                             |
| ---------------------------- | ------------------------------------------------------------------ |
| **PKCE (S256)**              | Code verifier stored server-side, never exposed to the client      |
| **State parameter**          | CSRF protection with cryptographically random state, 10-minute TTL |
| **Nonce validation**         | Prevents token replay attacks                                      |
| **Client secret encryption** | AES-256-GCM encrypted at rest                                      |
| **DNS verification**         | Prevents unauthorized domain claiming                              |
| **One-time tokens**          | 60-second TTL, single use for session bridging                     |
