# Auth0 SSO Configuration Guide

This guide explains how to configure Auth0 Single Sign-On (SSO) for Investibook, restricted to `@pingcap.com` email addresses.

---

## Overview

Investibook supports two authentication methods:

- **Email + Password** — always available (requires the `ADMIN_SECRET` env var to pre-create users)
- **Google SSO via Auth0** — enabled when `AUTH0_DOMAIN` and `AUTH0_CLIENT_ID` env vars are set

When Auth0 is configured, a "Sign in with Google" button appears on the login page. Domain enforcement (`@pingcap.com` only) is applied in two layers:

1. **Auth0 Post-Login Action** — denies the session before the token is issued
2. **Backend `/api/auth/exchange`** — rejects tokens whose email does not end with `@pingcap.com`

---

## Auth0 Dashboard Setup

### Step 1 — Create an Application

1. Log in to [Auth0 Dashboard](https://manage.auth0.com/).
2. Go to **Applications → Create Application**.
   - Name: e.g. `Investibook Prod`
   - Type: **Single Page Application**
3. In the **Settings** tab, fill in the following fields (comma-separated for multiple environments):

   | Field | Value |
   |-------|-------|
   | Allowed Callback URLs | `https://your-domain/auth/callback` |
   | Allowed Logout URLs | `https://your-domain` |
   | Allowed Web Origins | `https://your-domain` |

4. Note down the **Domain** and **Client ID** — you will need them as env vars.

### Step 2 — Enable Google Social Connection

1. Go to **Authentication → Social**.
2. Enable **Google / Gmail**.
   - For local development you can use Auth0's built-in Dev Keys.
   - For production, create a Google OAuth 2.0 client in [Google Cloud Console](https://console.cloud.google.com/) and paste the Client ID / Secret here.

### Step 3 — Create a Post-Login Action (domain restriction)

This is the critical server-side enforcement step.

1. Go to **Actions → Library → Create Action**.
   - Name: `Restrict to PingCAP domain`
   - Trigger: **Login / Post Login**
2. Paste the following code:

```javascript
exports.onExecutePostLogin = async (event, api) => {
  const email = event.user.email ?? '';
  if (!email.toLowerCase().endsWith('@pingcap.com')) {
    api.access.deny(
      'Access is restricted to PingCAP employees (@pingcap.com accounts only).'
    );
  }
};
```

3. Click **Deploy**.
4. Go to **Actions → Flows → Login**, drag the `Restrict to PingCAP domain` action into the flow, and click **Apply**.

---

## Dev vs. Prod Environments

The recommended approach is **one Auth0 tenant, two Applications** — this keeps user databases and audit logs separate while sharing the same Google connection and Post-Login Action.

| | Dev Application | Prod Application |
|---|---|---|
| Name | `Investibook Dev` | `Investibook Prod` |
| Callback URL | `http://localhost:3000/auth/callback` | `https://your-domain/auth/callback` |
| Logout URL | `http://localhost:3000` | `https://your-domain` |
| Web Origin | `http://localhost:3000` | `https://your-domain` |
| `AUTH0_CLIENT_ID` | `<dev-client-id>` | `<prod-client-id>` |
| `AUTH0_DOMAIN` | same tenant domain | same tenant domain |

Both Applications attach the same **Post-Login Action**, so domain restriction is enforced everywhere.

---

## Environment Variables

### Local development — `.env.local`

Create this file at the project root (it is already listed in `.gitignore` and will never be committed):

```bash
# .env.local
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=<dev-client-id>
```

Then start the frontend as usual:

```bash
make web          # or: pnpm run dev
```

### Production — Kubernetes / Docker

Inject the variables as container environment variables (e.g. via a Kubernetes Secret):

```yaml
env:
  - name: AUTH0_DOMAIN
    valueFrom:
      secretKeyRef:
        name: investibook-auth0
        key: domain
  - name: AUTH0_CLIENT_ID
    valueFrom:
      secretKeyRef:
        name: investibook-auth0
        key: client_id
```

The Next.js API route `GET /api/config` reads these at **request time** (not build time), so the same Docker image works across all environments without rebuilding.

---

## Security Layers

```
User clicks "Sign in with Google"
        │
        ▼
  Google OAuth consent screen
  (hd=pingcap.com hint — narrows account picker, not a security guarantee)
        │
        ▼
  Auth0 Post-Login Action
  ← BLOCKS non-@pingcap.com immediately, token never issued
        │
        ▼
  Frontend /auth/callback receives access token
        │
        ▼
  Backend POST /api/auth/exchange
  ← BLOCKS non-@pingcap.com as a second defence layer
        │
        ▼
  Investibook session token issued, user redirected to home
```

| Layer | Location | What it does |
|-------|----------|-------------|
| `hd` parameter | `frontend/app/login/page.tsx` | Hints Google's account picker to show `@pingcap.com` accounts (UX only) |
| Post-Login Action | Auth0 Dashboard | Server-side deny before token is issued |
| Domain check | `backend/investibook/api/auth.py` `/api/auth/exchange` | Rejects any token whose email is not `@pingcap.com` |

---

## Disabling Auth0 (email-only mode)

If `AUTH0_DOMAIN` or `AUTH0_CLIENT_ID` is unset or empty, the frontend will not render the Auth0Provider and the "Sign in with Google" button will be hidden automatically. The email + password form remains fully functional.
