# Turnstile Configuration Guide

Cloudflare Turnstile is **optional** and can be completely disabled for development or self-hosted deployments.

## Quick Config

### Disable Turnstile (Recommended for Development)

```env
PUBLIC_ENABLE_TURNSTILE=false
```

**What happens:**
- ✅ No Turnstile widget loads
- ✅ No captcha required to log in
- ✅ Login buttons work immediately
- ✅ OAuth URLs don't include `token` parameter
- ✅ No external script loaded from Cloudflare
- ✅ Completely works offline

**Perfect for:**
- Local development
- Self-hosted deployments
- Testing without internet

---

### Enable Turnstile (Production)

```env
PUBLIC_ENABLE_TURNSTILE=true
PUBLIC_TURNSTILE_SITE_KEY=0x4AAAAAABpqJe8FO0N84q0F
```

**What happens:**
- 🔒 Turnstile widget loads on login page
- 🔒 User must complete captcha before login
- 🔒 OAuth URLs include `?token=...` parameter
- 🔒 Script loaded from `challenges.cloudflare.com`

**Perfect for:**
- Production deployments
- Bot protection
- Public-facing instances

---

## How It Works

### Frontend Behavior

**When `ENABLE_TURNSTILE=false`:**
```typescript
// Turnstile.svelte
if (!ENABLE_TURNSTILE) {
  captcha.set('turnstile-disabled');  // Dummy token
}

// LoginForm.svelte
function getOAuthUrl(provider) {
  let url = `${API_URL}/auth/${provider}`;
  // No token parameter added
  return url;
}
```

**When `ENABLE_TURNSTILE=true`:**
```typescript
// Turnstile.svelte loads script
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" />

// LoginForm.svelte
function getOAuthUrl(provider) {
  let url = `${API_URL}/auth/${provider}?token=${captchaToken}`;
  return url;
}
```

### Backend Requirements

Your backend should handle both cases:

```typescript
// Example: src/routes/auth.ts
app.post("/auth/google", async (req, res) => {
  const turnstileToken = req.query.token;

  // If Turnstile is enabled in your backend, validate token
  if (process.env.ENABLE_TURNSTILE === 'true') {
    if (!turnstileToken) {
      return res.status(400).json({ error: "Turnstile token required" });
    }

    // Validate with Cloudflare API
    const isValid = await validateTurnstileToken(turnstileToken);
    if (!isValid) {
      return res.status(403).json({ error: "Invalid captcha" });
    }
  }

  // Continue with OAuth flow...
});
```

---

## Getting a Turnstile Site Key

If you want to enable Turnstile:

1. Go to https://dash.cloudflare.com
2. Select "Turnstile" from the sidebar
3. Create a new site
4. Copy the **Site Key** (starts with `0x4`)
5. Add to `.env`:
   ```env
   PUBLIC_TURNSTILE_SITE_KEY=0x4AAAAAAXXXXXXXXXXXXXXXX
   ```

**Note:** The example key `0x4AAAAAABpqJe8FO0N84q0F` is from the original compiled frontend and may not work for you. Get your own key.

---

## Build-Time vs Runtime

**Important:** Turnstile config is **build-time only**. It's embedded into the JavaScript bundle.

To change Turnstile settings:
1. Update `.env`
2. Rebuild: `pnpm build`
3. Restart backend

You **cannot** change it at runtime without rebuilding.

---

## Security Considerations

### With Turnstile Disabled

**Pros:**
- Simpler development
- Works offline
- No external dependencies
- No privacy concerns

**Cons:**
- No bot protection on login
- Vulnerable to automated account creation
- Anyone can spam OAuth endpoints

**Recommendation:** Fine for development and private deployments. Not recommended for public production.

### With Turnstile Enabled

**Pros:**
- Bot protection
- Rate limiting
- Industry standard (Cloudflare)

**Cons:**
- Requires internet connection
- External dependency
- Cloudflare can track users (privacy concern)
- Adds friction to login flow

**Recommendation:** Good for public production deployments with high traffic.

---

## Environment Variable Reference

| Variable | Type | Default | Description |
|----------|------|---------|-------------|
| `PUBLIC_ENABLE_TURNSTILE` | `'true'` \| `'false'` | `'false'` | Enable/disable Turnstile |
| `PUBLIC_TURNSTILE_SITE_KEY` | string | `'0x4AAAAAABpqJe8FO0N84q0F'` | Cloudflare site key |

**Note:** Must be strings `'true'` or `'false'`, not booleans.

---

## Testing

### Test with Turnstile Disabled
```bash
# .env
PUBLIC_ENABLE_TURNSTILE=false

# Build and test
pnpm build
cd .. && pnpm start
# Open http://localhost:3000/join
# Should see login buttons immediately clickable
```

### Test with Turnstile Enabled
```bash
# .env
PUBLIC_ENABLE_TURNSTILE=true
PUBLIC_TURNSTILE_SITE_KEY=your_key_here

# Build and test
pnpm build
cd .. && pnpm start
# Open http://localhost:3000/join
# Should see Turnstile widget before buttons work
```

---

## Troubleshooting

### "Turnstile failed to load"
- Check `PUBLIC_TURNSTILE_SITE_KEY` is valid
- Ensure internet connection
- Check browser console for errors
- Verify CSP allows `challenges.cloudflare.com`

### "Login buttons don't work"
- If Turnstile is enabled, complete captcha first
- If disabled, check that `captcha` store has value
- Check browser console for errors

### "Token parameter missing"
- Set `PUBLIC_ENABLE_TURNSTILE=false` if you don't want Turnstile
- Rebuild after changing `.env`

---

## CSP Configuration

If you enable Turnstile, your CSP must allow:

```html
<meta http-equiv="Content-Security-Policy"
      content="script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' https://challenges.cloudflare.com blob:" />
```

This is already configured in `src/app.html`.

---

## Alternative: reCAPTCHA

Want to use Google reCAPTCHA instead? You'll need to:

1. Create a new `Recaptcha.svelte` component
2. Update `LoginForm.svelte` to use it
3. Load reCAPTCHA script instead of Turnstile
4. Update backend to validate reCAPTCHA tokens

The architecture supports swapping captcha providers easily.

---

## Summary

**For Development:**
```env
PUBLIC_ENABLE_TURNSTILE=false
```
→ No captcha, login works immediately

**For Production (with bot protection):**
```env
PUBLIC_ENABLE_TURNSTILE=true
PUBLIC_TURNSTILE_SITE_KEY=your_cloudflare_key
```
→ Captcha required before login

**For Production (without bot protection):**
```env
PUBLIC_ENABLE_TURNSTILE=false
```
→ Same as development, but publicly accessible

Choose based on your needs!
