Skip to content

# Pocket-ID — OIDC Identity Provider

CT 109 "ops" · `192.168.1.8:11000` · `https://id.nuclide.systems`
Migrated CT 104 → CT 110 on 2026-05-20; CT 110 → CT 109 on 2026-05-26. SQLite-only (no Postgres). Compose at `/opt/stacks/pocketid/` on CT 109.

## What it does

Pocket-ID is a lightweight OIDC 2.1 / OAuth 2.0 IdP. Every service that supports OIDC can delegate login to it — one account, one MFA setup, SSO across the homelab. Clients are managed via an admin UI; there is no API key / scripted client creation.

## Admin access

- URL: `https://id.nuclide.systems` (admin panel is the default view when logged in as admin)
- Admin user: `fkrebs@nucli.de`
- API key: `1ed1d53a2c1b3fcbafea46863b0b9e88a649422b67541c764481c03918b60bc6` — header `X-API-Key` (not Bearer); stored as SHA-256 hash in SQLite `api_keys` table on CT 105

## OIDC endpoints (standard discovery)

Endpoint | URL
--- | ---
Discovery | `https://id.nuclide.systems/.well-known/openid-configuration`
Authorization | `https://id.nuclide.systems/authorize`
Token | `https://id.nuclide.systems/api/oidc/token`
Userinfo | `https://id.nuclide.systems/api/oidc/userinfo`
JWKS | `https://id.nuclide.systems/api/oidc/jwks`

## Creating a new OIDC client

1. Go to `https://id.nuclide.systems` → OIDC Clients → New Client
2. Fill in:
3. Name: descriptive (e.g. "Homarr", "Grafana")
4. Redirect URIs: the callback URL the service expects (see per-service table below)
5. PKCE: enable if the service supports it (preferred)
6. Copy the Client ID and Client Secret — secret is shown once.
7. Paste into the service's env vars (see patterns below).

> Pocket-ID 2.7.0+ stores secrets as bcrypt hashes — the plaintext secret is only visible at creation time. If lost, regenerate in the client edit view.

## Current clients

Service | CT | Client ID | Redirect URI | Notes
--- | --- | --- | --- | ---
Gitea | 104 | `9444609e-6151-4296-aaeb-576da886c887` | `https://git.nuclide.systems/user/oauth2/pocket-id/callback` | SSO active; local password sign-in disabled
Coder | 104 | `0aee4280-da5e-4782-a790-c7565c6c1366` | `https://dev.nuclide.systems/api/v2/users/oidc/callback` | SSO active; password auth disabled
n8n | 104 | `33135ad4-a3ed-45d3-938f-639abd2b9663` | `https://n8n.nuclide.systems/rest/oauth2-credential/callback` | encryption key rotated 2026-05-22
Vaultwarden | 104 | `7cda8d60-9bfa-44ca-ae9b-8ae436024a8b` | `https://vault.nuclide.systems/identity/connect/token` | created 2026-05-21; auth flow not yet wired
~~Homarr~~ | ~~109~~ | ~~`63a94e30-7bbf-4511-9a4c-82d992633427`~~ | — | DECOMMISSIONED — replaced by Homepage
Grafana | 109 | `92d987d5-d066-4e19-8fa1-960114c1244c` | `http://192.168.1.8:3000/login/generic_oauth` | SSO active 2026-05-23; PKCE enabled; LAN alias added 2026-05-24
Infisical | 109 | `b2069075-ede2-4251-ad1f-9a62e6a188b3` | `http://192.168.1.8:8200/api/v1/sso/oidc/callback` | migrated CT112→CT109 2026-05-26; manual OIDC config still pending
Proxmox VE | host | `38469e7e-1fff-4841-83a9-74bf38d847eb` | `https://192.168.1.20:8006` | LAN alias added 2026-05-24
Nextcloud | 105 | `a14b8076-985c-4989-95f8-e0283bfbdf32` | `https://nc.nuclide.systems/apps/oidc_login/oidc` |
Immich | 104 | `9c91c18b-e009-4371-9c54-b71d54e3c77a` | `https://photos.nuclide.systems/auth/login` |
Karakeep | 104 | `d92f82b0-b876-48c2-b3b0-05dd35fdf908` | `https://bookmarks.nuclide.systems/api/auth/callback/custom-server` |
Audiobookshelf | 104 | `cbbf20d5-d15c-419c-8f18-82d2fd7e810f` | `https://abs.nuclide.systems/auth/openid/callback` |
Shelfarr | 104 | `d8733fcc-eee8-42c4-b976-cb14e4e87693` | `https://shelfarr.nuclide.systems/auth/callback` |
Memos | 104 | `62bf4e0d-f0fe-4453-b0da-59eeea2bb69d` | `https://memos.nuclide.systems/auth/callback` |
Open WebUI | 104 | `e41534ae-994a-4188-8d42-31690c354284` | `https://chat.nuclide.systems/oauth/oidc/callback` |
Portainer | 109 | `bdf8b019-072e-4c21-b1fb-ad9c5ad392dc` | `http://192.168.1.8:9000/` | configured 2026-05-26; secret `DZu1JrzEpChU3Deeh0ycaV29s5aBQLGR`
Bifrost MCP | 104 | `ec0d15e6-e86d-49b0-ac12-cdfb5afc9086` | `https://ai.nuclide.systems` | per_user_oauth (not currently active — all MCP clients use auth_type=none)
mcp-auth | 104 | `af2f837b-8a77-4f6f-80b0-71b734eb7b0b` | — | internal; no launch URL
nuc-ai | 104 | `82ca2d53-6df4-4671-875c-fee85b54b76f` | — | internal; no launch URL

## Env var patterns per service type

### Homarr (v1 homarr-labs)

    AUTH_PROVIDERS=credentials,oidc   # plural; comma-separated list. Singular AUTH_PROVIDER is ignored.
    AUTH_OIDC_ISSUER=https://id.nuclide.systems
    AUTH_OIDC_CLIENT_ID=<client-id>
    AUTH_OIDC_CLIENT_SECRET=<client-secret>
    AUTH_OIDC_SCOPE=openid profile email
    AUTH_OIDC_NAME=Pocket-ID

### Grafana

    GF_AUTH_GENERIC_OAUTH_ENABLED=true
    GF_AUTH_GENERIC_OAUTH_NAME=Pocket-ID
    GF_AUTH_GENERIC_OAUTH_CLIENT_ID=<client-id>
    GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=<client-secret>
    GF_AUTH_GENERIC_OAUTH_SCOPES=openid profile email
    GF_AUTH_GENERIC_OAUTH_AUTH_URL=https://id.nuclide.systems/authorize
    GF_AUTH_GENERIC_OAUTH_TOKEN_URL=https://id.nuclide.systems/api/oidc/token
    GF_AUTH_GENERIC_OAUTH_API_URL=https://id.nuclide.systems/api/oidc/userinfo
    GF_AUTH_SIGNOUT_REDIRECT_URL=https://id.nuclide.systems/logout
    GF_AUTH_GENERIC_OAUTH_USE_PKCE=true
    GF_AUTH_GENERIC_OAUTH_AUTO_LOGIN=false
    GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH=contains(groups[*], 'admins') && 'Admin' || 'Viewer'

### Gitea

    [oauth2]
    ENABLED = true

    [service]
    ENABLE_PASSWORD_SIGNIN_FORM = false

Auth source added via Gitea admin UI → Authentication Sources → OAuth2 → OpenID Connect.

### Coder

    CODER_OIDC_ISSUER_URL=https://id.nuclide.systems
    CODER_OIDC_CLIENT_ID=<client-id>
    CODER_OIDC_CLIENT_SECRET=<client-secret>
    CODER_OIDC_SCOPES=openid,profile,email
    CODER_DISABLE_PASSWORD_AUTH=true

### Generic (any service with standard OIDC)

    Issuer:        https://id.nuclide.systems
    Auth URL:      https://id.nuclide.systems/authorize
    Token URL:     https://id.nuclide.systems/api/oidc/token
    Userinfo URL:  https://id.nuclide.systems/api/oidc/userinfo
    JWKS URL:      https://id.nuclide.systems/api/oidc/jwks
    Scopes:        openid profile email

## Backup

Pocket-ID SQLite DB + signing keys are backed up nightly via Backrest (CT 103). Pre-backup hook SSHs to CT 110, runs `pocket-id export` inside the container, copies the ZIP + signing keys to UNAS staging path, then the `services-backup-plan` snapshots to JottaCloud. See `services/backrest.md`.

## Stack location

CT 110 (`192.168.1.5`): `/opt/stacks/pocketid/docker-compose.yml`

    # key env vars
    POCKET_ID_URL=https://id.nuclide.systems
    TRUST_PROXY=true
    MAXMIND_LICENSE_KEY=...  # optional geo-IP