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

# Custom Authentication

> Integrate your authentication system with Cadana for seamless Single Sign-On (SSO)

export const ApiExample = ({method = "GET", path, params, body, reference, tenantKey}) => {
  const baseUrl = "https://api.cadanapay.com";
  const query = params ? "?" + Object.entries(params).map(([k, v]) => `${k}=${v}`).join("&") : "";
  const url = `${baseUrl}${path}${query}`;
  const isPlatformPath = (/^\/(v1\/)?platform(\/|$)/).test(path || "");
  const includeTenantKey = tenantKey === undefined ? !isPlatformPath : tenantKey;
  let curl = `curl -X ${method} '${url}' \\\n  -H 'Authorization: Bearer YOUR_API_KEY'`;
  if (includeTenantKey) {
    curl += ` \\\n  -H 'X-MultiTenantKey: YOUR_BUSINESS_TENANT_KEY'`;
  }
  if (body) {
    const formatted = JSON.stringify(body, null, 2);
    curl += ` \\\n  -H 'Content-Type: application/json' \\\n  -d '${formatted}'`;
  }
  return <div>
      <CodeBlock language="bash" filename="bash" wrap>
        {curl}
      </CodeBlock>
      {reference && <div style={{
    marginTop: "-0.5rem",
    marginBottom: "1rem"
  }}>
          <a href={reference} style={{
    fontSize: "0.875rem"
  }}>
            Try it in the playground →
          </a>
        </div>}
    </div>;
};

Custom authentication lets you connect your own identity provider to Cadana so users can access your white-label app without creating separate Cadana credentials. Users log into your system, and you exchange their JWT for a Cadana session — a seamless Single Sign-On (SSO) experience.

This is the standard approach for [White-Label UI](/white-label/overview) integrations where Cadana hosts a fully branded web app on your subdomain (e.g., `payroll.yourcompany.com`).

***

## Prerequisites

<Steps>
  <Step title="JWT-compliant auth system">
    Your identity provider must issue standard JWTs and expose a **JWKS (JSON Web Key Set)** endpoint for signature verification. Cadana supports popular providers including **Auth0**, **AWS Cognito**, and **Stytch**. Custom engines are also supported — contact your account manager to confirm compatibility.
  </Step>

  <Step title="API key from Dashboard">
    Get your API key from the [Cadana Dashboard](https://app.cadanapay.com). See [Authentication](/authentication) for details.
  </Step>

  <Step title="White-label platform configured">
    You need a white-label setup with a custom domain (or use the default Cadana domain).
  </Step>
</Steps>

<Warning>
  Any custom authentication engine must be **ISO and SOC 2 compliant** to ensure security and compatibility with Cadana's platform.
</Warning>

***

## How It Works

1. **User logs in** to your application
2. **You issue a JWT** with the user's identity
3. **You exchange the JWT** with Cadana for a one-time redirect token
4. **You redirect the user** to the branded white-label app with the token
5. **Cadana validates the token** and establishes an active session

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant P as Your App
    participant C as Cadana API
    participant W as White-Label App

    U->>P: 1. Log in
    P->>P: 2. Issue JWT
    P->>C: 3. Exchange JWT (POST /v1/auth/login/jwt)
    C-->>P: 4. Redirect token
    P->>W: 5. Redirect to White-Label App
    W->>C: 6. Establish session
    W-->>U: 7. Access granted
```

***

## Step 1: Configure SSO Settings

Configure the following settings for your platform:

| Setting               | Description                                   | Example                                              |
| :-------------------- | :-------------------------------------------- | :--------------------------------------------------- |
| **Auth Issuer (ISS)** | Your identity provider's issuer URL           | `https://auth.yourcompany.com`                       |
| **JWKS Endpoint**     | URL where Cadana fetches your public keys     | `https://auth.yourcompany.com/.well-known/jwks.json` |
| **Login URL**         | Where Cadana redirects when a session expires | `https://yourcompany.com/login`                      |
| **Logout URL**        | Where Cadana redirects when a user logs out   | `https://yourcompany.com/logout`                     |

<Note>
  SSO settings are currently configured with the help of your Cadana account manager. Self-service configuration in the Dashboard is coming soon.
</Note>

Cadana uses the ISS and JWKS endpoint to validate and decode your JWTs. Your auth engine must be able to mint standard JWTs and expose verification metadata at the JWKS endpoint.

***

## Step 2: Create the User with SSO

When onboarding a user who will access the white-label app via SSO, first create their Person and User records. See [Onboard Workers](/workforce/onboarding-workers) for the full Person creation flow.

Once the Person exists, create the User with [`POST /v1/users/invite`](/api-reference/workforce/users/invite). Set `suppressWelcomeEmail` to `true` so they don't receive the default Cadana sign-up email — they'll log in through your system instead.

<ApiExample method="POST" path="/v1/users/invite" body={{ personId: "8ef9a712-cdae-4110-b1ea-9ba95abbee6e", suppressWelcomeEmail: true }} reference="/api-reference/workforce/users/invite" />

***

## Step 3: Attach Your Auth ID to the User

For every user, attach your internal auth identifier as the `sub` (subject) on their Cadana User record. This is the critical step that ties your identity system to Cadana's.

Use [`PUT /v1/users/{userId}/sub`](/api-reference/workforce/users/attach-a-custom-sub) with the `tokenSub` field set to your internal user ID — the same value that appears in the `sub` claim of your JWTs.

<ApiExample method="PUT" path="/v1/users/a1b2c3d4-e5f6-7890-abcd-ef1234567890/sub" body={{ tokenSub: "your-internal-user-id-12345" }} reference="/api-reference/workforce/users/attach-a-custom-sub" />

Returns `204` on success.

<Warning>
  The `tokenSub` value must exactly match the `sub` claim in the JWTs you issue for this user. If they don't match, the token exchange in the next step will fail.
</Warning>

***

## Step 4: Exchange JWT for Redirect Token

When a user needs to access the white-label app, exchange their JWT for a Cadana redirect token using [`POST /v1/auth/login/jwt`](/api-reference/workforce/custom-auth/jwt).

<ApiExample method="POST" path="/v1/auth/login/jwt" body={{ jwt: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleS0xMjM0NSJ9.eyJpc3MiOiJodHRwczovL2F1dGgueW91cmNvbXBhbnkuY29tIiwic3ViIjoieW91ci1pbnRlcm5hbC11c2VyLWlkLTEyMzQ1IiwiZXhwIjoxNzA5MjQ2NDAwLCJpYXQiOjE3MDkyNDI4MDB9.signature" }} reference="/api-reference/workforce/custom-auth/jwt" />

**Response:**

```json theme={null}
{
  "redirectToken": "eybzym7hwk..."
}
```

### JWT Requirements

Your JWT must include these claims:

| Claim | Required | Description                                                                      |
| :---- | :------- | :------------------------------------------------------------------------------- |
| `iss` | Yes      | Issuer — must match the Auth Issuer you configured in Step 1                     |
| `sub` | Yes      | Subject — the user's unique ID, must match the `tokenSub` you attached in Step 3 |
| `exp` | Yes      | Expiration timestamp                                                             |
| `iat` | Yes      | Issued-at timestamp                                                              |

<Tip>
  The JWT header must include a `kid` (Key ID) field. The `kid` must exactly match a key in your JWKS endpoint so Cadana can verify the signature.
</Tip>

***

## Step 5: Redirect to the White-Label App

Use the redirect token to send the user to the white-label app. The token is one-time use — once consumed, it establishes an active session.

**Default Cadana domain:**

```
https://app.cadanapay.com/login?redirectToken={{redirectToken}}
```

**Custom domain:**

```
https://payroll.yourcompany.com/login?redirectToken={{redirectToken}}
```

Once the user lands on this URL, Cadana exchanges the redirect token for an active session. The user gets full access to the branded white-label app without any additional login.

***

## Session Management

Cadana manages session lifecycle automatically. When a session event occurs, Cadana redirects the user to the URLs you configured in Step 1:

| Event           | Redirect destination                                                       |
| :-------------- | :------------------------------------------------------------------------- |
| Session expires | Your **Login URL** — user re-authenticates and you repeat the JWT exchange |
| User logs out   | Your **Logout URL** — handle cleanup in your app                           |

***

## Full Integration Example

Here's the complete flow for onboarding a user and enabling SSO access:

```mermaid theme={null}
sequenceDiagram
    participant P as Your Platform
    participant C as Cadana API
    participant U as User
    participant W as White-Label App

    Note over P,W: One-time setup per user

    P->>C: 1. Create Person + User
    Note right of P: suppressWelcomeEmail: true
    C-->>P: personId, userId

    P->>C: 2. Attach sub (PUT /v1/users/{id}/sub)
    Note right of P: tokenSub: your internal user ID
    C-->>P: 204 OK

    Note over P,W: Every login session

    U->>P: 3. Log in to your app
    P->>P: 4. Issue JWT (iss, sub, exp, iat, kid)

    P->>C: 5. Exchange JWT (POST /v1/auth/login/jwt)
    C-->>P: redirectToken

    P->>W: 6. Redirect user to White-Label App
    Note right of P: payroll.yourcompany.com/login?redirectToken=...
    W-->>U: 7. Active session established
```

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Custom Domain" icon="globe" href="/white-label/custom-domain">
    Set up a custom domain for the white-label app
  </Card>

  <Card title="Onboard Workers" icon="user-plus" href="/workforce/onboarding-workers">
    Create Person and User records
  </Card>

  <Card title="White-Label UI Overview" icon="palette" href="/white-label/overview">
    Customize the white-label app for your brand
  </Card>
</CardGroup>
