# KiteStacks Authentik SSO Setup

**Established:** 2026-06-08  
**Author:** kenpat  
**Status:** In Progress — config files deployed, manual Authentik UI steps pending

---

## Architecture

All services sit behind Cloudflare Tunnels on the `kitestacks` Docker network.  
`cloudflared` routes external traffic directly to each service container by hostname.  
Authentik (`authentik:9000`) is the single identity provider.

```
Internet → Cloudflare → cloudflared → [service container]
                                          ↕
                                 Authentik auth check
```

**Two integration patterns are used:**

| Pattern | How it works | Services |
|---------|-------------|---------|
| Native OIDC/OAuth2 | App calls Authentik directly for login | Grafana, OpenWebUI, Forgejo, OpenProject, Kavita |
| Authentik Proxy Provider | Cloudflare tunnel → Authentik (embedded outpost) → service | Shaarli, Uptime Kuma, LiteLLM |

---

## SSO Status

| Service | Subdomain | Port | Method | Status |
|---------|-----------|------|--------|--------|
| Authentik | auth.kitestacks.com | 9000 | (is the IdP) | ✅ Running |
| Grafana | grafana.kitestacks.com | 3000 | OAuth2 | ⚠️ env set, Authentik app needed |
| Kite AI (OpenWebUI) | ai.kitestacks.com | 8080 | OIDC | ⚠️ env set, Authentik app needed |
| Forgejo | gitforge.kitestacks.com | 3000 | OAuth2 | ⚠️ Forgejo admin UI config needed |
| BookStack | — | — | — | 🚫 Retired — books hosted on Kavita |
| OpenProject | tasks.kitestacks.com | 80 | OIDC | ⚠️ env set, Authentik app needed |
| Kavita | kavita.kitestacks.com | 5000 | OIDC | ⚠️ appsettings.json updated, Authentik app needed |
| Shaarli | links.kitestacks.com | 80 | Proxy | ⚠️ Authentik Proxy Provider needed + CF tunnel update |
| Uptime Kuma | status.kitestacks.com | 3001 | Proxy | ⚠️ Authentik Proxy Provider needed + CF tunnel update |
| LiteLLM | llm.kitestacks.com | 4000 | Proxy | ⚠️ Authentik Proxy Provider needed + CF tunnel update |
| Portainer | portainer.kitestacks.com | 9000 | — | 🚫 SSO excluded |
| Prometheus | prometheus.kitestacks.com | 9090 | — | 🚫 SSO excluded |
| Node Exporter | node-exporter.kitestacks.com | 9100 | — | 🚫 SSO excluded |
| OpenRouter | openrouter.ai | — | — | 🚫 external, excluded |

*BookStack has been retired. All books are hosted on Kavita (`kavita.kitestacks.com`).

---

## Files Changed (2026-06-08)

| File | What changed |
|------|-------------|
| `apps/authentik/docker-compose.yml` | Added `kitestacks` network declaration, `AUTHENTIK_ERROR_REPORTING__ENABLED: false` |
| `apps/kavita/config/appsettings.json` | `OpenIdConnectSettings.Enabled` → `true`, `Authority` set |
| `apps/bookstack/docker-compose.yml` | OIDC env vars added, `kitestacks` network added, `APP_URL` updated |
| `apps/bookstack/.env` | Created with `BOOKSTACK_OIDC_SECRET` placeholder |
| `apps/openproject/docker-compose.yml` | OIDC env vars added, `kitestacks` network declared |
| `apps/openproject/.env` | Created with `OPENPROJECT_OIDC_SECRET` placeholder |

---

## Step 1 — Authentik Admin UI: Create Providers and Applications

Go to **https://auth.kitestacks.com** → Admin Interface.

### For each native OIDC service, create one OAuth2/OpenID Provider:

**General settings (all providers):**
- Signing Key: select the default RS256 key
- Token validity: 10 minutes (access), 30 days (refresh)

#### 1. Grafana

- **Providers → Create → OAuth2/OpenID Provider**
  - Name: `Grafana`
  - Client type: `Confidential`
  - Client ID: `grafana`
  - Redirect URIs: `https://grafana.kitestacks.com/login/generic_oauth`
  - Scopes: `openid`, `email`, `profile`
- **Applications → Create**
  - Name: `Grafana`, Slug: `grafana`
  - Provider: `Grafana` (above)
- Copy the **Client Secret** → put it in `/home/kenpat/docker/grafana/.env`:
  ```
  GRAFANA_OAUTH_CLIENT_SECRET=<paste_secret>
  ```
- Restart: `cd ~/docker/grafana && docker compose up -d`

#### 2. Kite AI (Open WebUI)

- **Providers → Create → OAuth2/OpenID Provider**
  - Name: `Open WebUI`, Client ID: `open-webui`
  - Redirect URIs: `https://ai.kitestacks.com/oauth/oidc/callback`
  - Scopes: `openid`, `email`, `profile`
- **Applications → Create**: Name: `Kite AI`, Slug: `open-webui`
- Copy secret → `/home/kenpat/docker/kite-ai/.env`:
  ```
  OPENWEBUI_OAUTH_CLIENT_SECRET=<paste_secret>
  ```
- Restart: `cd ~/docker/kite-ai && docker compose up -d`

#### 3. Kavita

- **Providers → Create → OAuth2/OpenID Provider**
  - Name: `Kavita`, Client ID: `kavita`
  - Redirect URIs: `https://kavita.kitestacks.com/api/Account/OIDCCallback`
  - Scopes: `openid`, `email`, `profile`
- **Applications → Create**: Name: `Kavita`, Slug: `kavita`
- Copy secret → `/home/kenpat/docker/kavita/config/appsettings.json`:
  ```json
  "Secret": "<paste_secret>"
  ```
- Restart: `cd ~/docker/kavita && docker compose restart` (if a compose exists) or `docker restart kavita`

#### 4. OpenProject

- **Providers → Create → OAuth2/OpenID Provider**
  - Name: `OpenProject`, Client ID: `openproject`
  - Redirect URIs: `https://tasks.kitestacks.com/auth/oidc/callback`
  - Scopes: `openid`, `email`, `profile`
- **Applications → Create**: Name: `OpenProject`, Slug: `openproject`
- Copy secret → `/home/kenpat/docker/openproject/.env`:
  ```
  OPENPROJECT_OIDC_SECRET=<paste_secret>
  ```
- Restart: `cd ~/docker/openproject && docker compose up -d`
  - **Note:** Container is currently running `openproject/community:13` but compose
    specifies `openproject/openproject:15`. Recreation will upgrade it. Verify data
    migration after restart. The Cloudflare tunnel for `tasks.kitestacks.com` may need
    updating from `openproject:8080` → `openproject:80` after the upgrade.

---

### For proxy services, create Proxy Providers:

These services have no native OIDC. Authentik will act as the reverse proxy.

**After creating each Proxy Provider + Application, add all three to the Embedded Outpost:**
Outposts → `authentik Embedded Outpost` → Edit → move all three proxy applications to "Selected Applications".

#### 6. Shaarli

- **Providers → Create → Proxy Provider**
  - Name: `Shaarli`
  - Mode: `Reverse Proxy`
  - External host: `https://links.kitestacks.com`
  - Internal host: `http://shaarli:80`
  - Internal host SSL validation: off
- **Applications → Create**: Name: `Shaarli`, Slug: `shaarli`
- **Cloudflare Tunnel**: Change `links.kitestacks.com` route from `http://shaarli:80` → `http://authentik:9000`

#### 7. Uptime Kuma

- **Providers → Create → Proxy Provider**
  - Name: `Uptime Kuma`
  - Mode: `Reverse Proxy`
  - External host: `https://status.kitestacks.com`
  - Internal host: `http://uptime-kuma:3001`
- **Applications → Create**: Name: `Uptime Kuma`, Slug: `uptime-kuma`
- **Cloudflare Tunnel**: Change `status.kitestacks.com` route from `http://uptime-kuma:3001` → `http://authentik:9000`

#### 8. LiteLLM *(when ready to expose publicly)*

- **Providers → Create → Proxy Provider**
  - Name: `LiteLLM`
  - Mode: `Reverse Proxy`
  - External host: `https://llm.kitestacks.com`
  - Internal host: `http://kite-litellm:4000`
- **Applications → Create**: Name: `LiteLLM`, Slug: `litellm`
- **Cloudflare Tunnel**: Add route `llm.kitestacks.com` → `http://authentik:9000`

---

## Step 2 — Forgejo: Add Authentik OAuth2 Source

Forgejo stores OAuth sources in its database (not in app.ini), so this requires the Forgejo admin UI.

1. Go to **https://gitforge.kitestacks.com** → Site Administration → Authentication Sources
2. Click **Add Authentication Source**
3. Settings:
   - Authentication Type: `OAuth2`
   - Authentication Name: `authentik`
   - OAuth2 Provider: `OpenID Connect`
   - Client ID: `forgejo`
   - Client Secret: *(create an Authentik app first — see below)*
   - OpenID Connect Auto Discovery URL:
     `https://auth.kitestacks.com/application/o/forgejo/.well-known/openid-configuration`
   - Additional scopes: `email profile`
4. Save

**Create the Authentik app for Forgejo first:**
- Provider: OAuth2/OpenID, Client ID: `forgejo`
- Redirect URI: `https://gitforge.kitestacks.com/user/oauth2/authentik/callback`
- Application slug: `forgejo`

---

## Step 3 — Cloudflare Tunnel Updates

In the Cloudflare Zero Trust Dashboard → Networks → Tunnels → your tunnel → Configure → Public Hostnames:

| Hostname | Change From | Change To |
|----------|------------|-----------|
| `links.kitestacks.com` | `http://shaarli:80` | `http://authentik:9000` |
| `status.kitestacks.com` | `http://uptime-kuma:3001` | `http://authentik:9000` |
| `llm.kitestacks.com` | (new) | `http://authentik:9000` |
| `tasks.kitestacks.com` | `http://openproject:8080` | `http://openproject:80` *(after OpenProject upgrade)* |

---

## Step 4 — Restart Containers After Config Changes

After filling in all secrets in the `.env` files:

```bash
cd ~/docker/grafana        && docker compose up -d
cd ~/docker/kite-ai        && docker compose up -d
cd ~/docker/bookstack      && docker compose up -d
cd ~/docker/openproject    && docker compose up -d
cd ~/docker/authentik      && docker compose up -d
docker restart kavita
```

---

## Troubleshooting

**OIDC redirect loop / 400 error**
- Check that the Redirect URI in the Authentik provider exactly matches what the app sends.
- Grafana: `https://grafana.kitestacks.com/login/generic_oauth` (no trailing slash)

**Kavita "OIDC is not configured" message**
- The `appsettings.json` change requires a container restart: `docker restart kavita`
- Verify `Enabled: true` and `Secret` is not empty.

**BookStack redirects to `http://192.168.1.205:6875`**
- `APP_URL` must be updated to the real HTTPS domain and the container recreated.

**OpenProject: OIDC provider not visible in login**
- Env var format is version-specific. If env vars don't work in v15, configure via:
  Administration → Authentication → OpenID Connect Providers → Add

**Proxy services (Shaarli/Uptime Kuma) show Authentik login but then 502**
- Verify the Embedded Outpost has the proxy providers assigned.
- Verify internal host is reachable: `docker exec authentik curl -v http://shaarli:80`

---

## Service-to-Container Name Reference

| Subdomain | Container Name | Internal Port |
|-----------|---------------|--------------|
| auth.kitestacks.com | authentik | 9000 |
| grafana.kitestacks.com | grafana | 3000 |
| ai.kitestacks.com | kite-openwebui | 8080 |
| gitforge.kitestacks.com | forgejo | 3000 |
| tasks.kitestacks.com | openproject | 80 (after upgrade) |
| kavita.kitestacks.com | kavita | 5000 |
| links.kitestacks.com | shaarli | 80 |
| status.kitestacks.com | uptime-kuma | 3001 |
| llm.kitestacks.com | kite-litellm | 4000 |
| www.kitestacks.com | homepage | 3000 |
| portainer.kitestacks.com | portainer | 9000 (excluded) |
| prometheus.kitestacks.com | prometheus | 9090 (excluded) |
| node-exporter.kitestacks.com | node-exporter | 9100 (excluded) |
