# Porchlight OpenID Connect Provider with user management, built with FastAPI. Porchlight handles user registration (via magic links), credential management (passwords and WebAuthn security keys), and issues OIDC tokens for relying parties. It uses SQLite for storage, Jinja2 + HTMX for the UI, and idpyoidc for the OIDC protocol layer. ## Production Setup ### Docker (recommended) ```bash docker compose --profile prod up --build ``` This starts Porchlight on port 8000 with 4 uvicorn workers. Data is persisted in a named Docker volume. Set required environment variables in `docker-compose.yml` or via `.env`: ```bash OIDC_OP_ISSUER=https://auth.example.com OIDC_OP_SESSION_SECRET= ``` ### Manual Requires Python 3.13+ and [uv](https://docs.astral.sh/uv/). ```bash uv sync --no-dev OIDC_OP_ISSUER=https://auth.example.com \ OIDC_OP_SESSION_SECRET=$(python -c "import secrets; print(secrets.token_hex(32))") \ uv run uvicorn porchlight.app:create_app \ --factory --host 0.0.0.0 --port 8000 --workers 4 ``` ### Bootstrap the first admin user After starting the app for the first time, create an admin account: ```bash OIDC_OP_ISSUER=https://auth.example.com \ uv run porchlight initial-admin admin ``` This prints a one-time registration URL. Open it in a browser to set up credentials (password or security key) for the admin user. ### CLI commands Porchlight includes a CLI for administrative tasks. All commands read the same `OIDC_OP_*` environment variables as the server. **`porchlight create-invite `** -- Generate a magic link registration URL for a new user. ```bash uv run porchlight create-invite alice uv run porchlight create-invite alice --ttl 3600 --note "Onboarding" ``` | Option | Description | |---|---| | `--ttl SECONDS` | Link expiration (default: `OIDC_OP_INVITE_TTL`, 86400s) | | `--note TEXT` | Optional note stored with the link | **`porchlight initial-admin `** -- Bootstrap the first admin user with a registration link. ```bash uv run porchlight initial-admin admin uv run porchlight initial-admin admin --group admin --group superusers ``` | Option | Description | |---|---| | `--group TEXT` | Groups to assign (repeatable, default: `admin`, `users`) | ### Configuration All settings are read from environment variables with the `OIDC_OP_` prefix: | Variable | Default | Description | |---|---|---| | `OIDC_OP_ISSUER` | **required** | OIDC issuer URL (must match public URL) | | `OIDC_OP_SESSION_SECRET` | random per process | Session cookie signing secret | | `OIDC_OP_DEBUG` | `false` | Enable `/docs` Swagger UI | | `OIDC_OP_SQLITE_PATH` | `data/oidc_op.db` | SQLite database path | | `OIDC_OP_SIGNING_KEY_PATH` | `data/keys` | OIDC signing key storage | | `OIDC_OP_INVITE_TTL` | `86400` | Magic link expiry in seconds | | `OIDC_OP_MANAGE_CLIENT_ID` | `manage-app` | Client ID for the management UI | Database migrations run automatically on startup. ## Development Setup ### Prerequisites - Python 3.13+ - [uv](https://docs.astral.sh/uv/) - Node.js (for e2e tests) ### Getting started ```bash # Install dependencies (including dev tools) uv sync # Start the dev server with hot reload OIDC_OP_ISSUER=http://localhost:8000 OIDC_OP_DEBUG=true \ uv run uvicorn porchlight.app:create_app \ --factory --host 127.0.0.1 --port 8000 --reload --reload-dir src ``` Or with Docker: ```bash docker compose --profile dev up --build ``` ### Running tests ```bash # Unit/integration tests uv run pytest # Lint and format uv run ruff check src/ tests/ --fix uv run ruff format src/ tests/ # Type checking uv run ty check src/ ``` ### End-to-end browser tests The e2e suite uses Playwright (Node.js) to test all user-facing flows against a running instance of the app. ```bash # One-time setup: install Playwright and Chromium cd tests/e2e npm install && npm run setup cd ../.. # Run all e2e tests ./tests/e2e/run.sh # Run a specific test ./tests/e2e/run.sh tests/e2e/test_login.js # Run with visible browser (not headless) E2E_HEADLESS=0 ./tests/e2e/run.sh ``` The runner starts the app on port 8099, seeds test fixtures into a temporary SQLite database, runs all `test_*.js` files, and tears everything down. ### Full quality check ```bash uv run ruff format src/ tests/ uv run ruff check src/ tests/ --fix uv run ty check src/ uv run pytest ./tests/e2e/run.sh ```