# RFID Database PWA + REST API for storing LF/HF RFID tag dumps organized by site. Use the web UI in the field, or call `/api/v1` from scripts (and a future Proxmark CLI). ## Features - Sites (A, B, C, …) with tag records (UID, protocol, keys, full dump) - Multi-user auth (shared workspace, no RBAC yet) — local password + optional OIDC - Personal API tokens (Settings UI) for Bearer auth - Export: JSON, Proxmark, MCT, hex - Import dumps + full JSON backup - Installable PWA with offline read of cached pages - Vitest unit/integration tests + GitHub Actions CI ## Quick start ```bash cp .env.example .env # set AUTH_SECRET to a long random string npm install npm run create-user -- --email you@lab.local --password 'changeme' --name You npm run dev ``` Open http://localhost:3000 and sign in. ## Environment | Variable | Required | Description | |----------|----------|-------------| | `AUTH_SECRET` | yes | NextAuth secret | | `AUTH_URL` | recommended | Public app URL (e.g. `https://rfid.example.com`) | | `RFID_DB_PATH` | no | SQLite path (default `./data/rfid.db`) | | `AUTH_OIDC_ISSUER` | for OIDC | Issuer URL (must expose `/.well-known/openid-configuration`) | | `AUTH_OIDC_CLIENT_ID` | for OIDC | OIDC client id | | `AUTH_OIDC_CLIENT_SECRET` | for OIDC | OIDC client secret | | `AUTH_OIDC_NAME` | no | SSO button label (default `SSO`) | ### Enabling OIDC 1. **Pull latest and rebuild** (OIDC button detection changed recently): ```bash git pull docker compose up -d --build ``` 2. In your IdP, create a **confidential** OIDC application. 3. Set the redirect / callback URI to **exactly**: ``` {AUTH_URL}/api/auth/callback/oidc ``` Example: `https://rfid.atlashorizon.net/api/auth/callback/oidc` 4. Put these in `.env`: ```bash AUTH_URL=https://rfid.atlashorizon.net AUTH_SECRET=...long random... AUTH_OIDC_ISSUER=https://auth.atlashorizon.net AUTH_OIDC_CLIENT_ID=rfiddb AUTH_OIDC_CLIENT_SECRET=... AUTH_OIDC_NAME=AtlasHorizon ``` 5. Restart, then verify the running app sees config (no secrets returned): ```bash curl -s https://rfid.atlashorizon.net/api/v1/auth/config | jq ``` You want `"oidcEnabled": true` and `"callbackUrl"` matching your IdP. 6. Open `/login` — you should see **Sign in with AtlasHorizon**. Notes: - `AUTH_OIDC_ISSUER` must match discovery (`{issuer}/.well-known/openid-configuration`). Your AtlasHorizon issuer at `https://auth.atlashorizon.net` is valid. - The IdP must return an **email** claim (we request `openid email profile`). - Local password login stays available alongside SSO. - If the button is missing, you are almost certainly on an old image — rebuild. ## REST API (`/api/v1`) Authenticate with a session cookie **or** `Authorization: Bearer rfid_…` (create tokens under **Settings → API tokens**). | Method | Path | Purpose | |--------|------|---------| | GET/POST | `/api/v1/sites` | List / create sites | | GET/PATCH/DELETE | `/api/v1/sites/{id}` | Site detail | | GET/POST | `/api/v1/tags` | List / create tags | | GET/PATCH/DELETE | `/api/v1/tags/{id}` | Tag detail | | PUT | `/api/v1/tags/by-uid/{uid}?siteId=` | Upsert by UID | | GET | `/api/v1/tags/{id}/export?format=` | `json` \| `proxmark` \| `mct` \| `hex` | | POST | `/api/v1/tags/import` | Parse dump → create tag | | GET | `/api/v1/search?q=` | Cross-site search | | GET/POST | `/api/v1/backup` | Full backup export / import | | GET/POST | `/api/v1/tokens` | List / create PATs | | DELETE | `/api/v1/tokens/{id}` | Revoke PAT | | GET | `/api/v1/me` | Current user | ### curl examples ```bash # Create a token in the UI first, then: export TOKEN=rfid_… export HOST=http://localhost:3000 curl -sH "Authorization: Bearer $TOKEN" "$HOST/api/v1/sites" curl -sH "Authorization: Bearer $TOKEN" \ "$HOST/api/v1/tags/$ID/export?format=proxmark" -o dump.json curl -sH "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -X PUT "$HOST/api/v1/tags/by-uid/04A1B2C3?siteId=$SITE" \ -d '{"label":"Dock fob","frequency":"HF","protocol":"MIFARE_CLASSIC_1K","uid":"04A1B2C3","dumpData":{"size":"1K","sectors":[]},"keys":{"A":["FFFFFFFFFFFF"]}}' ``` ## Tests & CI ```bash npm test npm run lint npm run typecheck npm run build ``` GitHub Actions on every pull request and push to `main`: 1. **lint / typecheck / test / Next.js build** 2. **Docker image** — build on PRs; on `main` (and `v*` tags) push to GHCR: `ghcr.io/chewbaccalakis/rfid-database` ### Pull the published image ```bash docker pull ghcr.io/chewbaccalakis/rfid-database:latest # or a specific commit: ghcr.io/chewbaccalakis/rfid-database:sha- ``` If the package is private, authenticate first: ```bash echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin ``` Or in Compose, set `image: ghcr.io/chewbaccalakis/rfid-database:latest` and comment out `build:`. ## Docker ### docker compose (recommended) ```bash cp .env.example .env # set AUTH_SECRET to a long random string # optional first user on boot: # echo 'CREATE_USER_EMAIL=admin@lab.local' >> .env # echo 'CREATE_USER_PASSWORD=changeme' >> .env docker compose up -d --build ``` App: http://localhost:3000 — SQLite persists in the `rfid-data` volume. Create a user later (if you skipped bootstrap): ```bash docker compose exec rfid-database \ npx tsx scripts/create-user.ts --email you@lab.local --password 'secret' --name You ``` Stop / remove (keeps volume): ```bash docker compose down ``` ### Plain docker ```bash docker build -t rfid-database . docker run --rm -p 3000:3000 \ -e AUTH_SECRET=your-long-secret \ -e CREATE_USER_EMAIL=admin@lab.local \ -e CREATE_USER_PASSWORD=changeme \ -v rfid-data:/data \ rfid-database ``` ## Security Tag dumps often include sector keys. Keep the app behind HTTPS, do not expose it publicly without auth, and treat `data/rfid.db` as sensitive.