docs: add comprehensive device authentication implementation details

- Add IMPLEMENTATION_EXACT.md with exact code changes for all phases
- Update IMPLEMENTATION_PLAN.md with clarifications on two-field approach:
  - device_identifier: Serial number (Kobo) or UUID (KOReader)
  - auth_token: Auto-generated API key for authentication
- Resolve all user questions with  marked decisions
- Add verification steps for documentation accuracy
- Document Kobo vs KOReader registration workflow differences
- Add SQL query for token regeneration (UpdateDeviceAuthToken)
- Include TypeScript device management code
- Add Bruno API test files for all new endpoints
- Update Kobo setup documentation for URL path token approach
This commit is contained in:
2026-02-12 15:36:18 -05:00
parent 1db89669fb
commit b941776677
3 changed files with 2457 additions and 34 deletions
File diff suppressed because it is too large Load Diff
+126 -33
View File
@@ -236,6 +236,7 @@ func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerF
- [ ] Return 401 if both methods fail
3. **Add Token Regeneration Backend** (`internal/handlers/devices.go`)
- [ ] Add new SQL query: `UpdateDeviceAuthToken` (DO NOT use UpdateDevice - it doesn't modify auth_token field)
- [ ] Add endpoint: `POST /api/devices/:id/regenerate-token`
- [ ] Generate new random UUID for auth_token
- [ ] Update device in database
@@ -499,7 +500,7 @@ More secure but requires plugin installation. Justified because KOReader users a
- `internal/handlers/devices.go` - Add regenerate token endpoint
- `internal/database/queries/queries.sql` - No changes needed (auth_token already exists)
- `templates/devices.templ` - Add copy token, regenerate token buttons
- `templates/partials/device-management.templ` - Kobo config instructions with API key
- Create `templates/devices-kobo-config.templ` - Kobo config instructions with API key
### Testing
- `cmd/server/tests/kobo_test.go` - Update for URL path token
@@ -607,6 +608,17 @@ Based on user feedback, the following decisions have been finalized:
**Note:** This is about device MANAGEMENT, not authentication. Authentication uses `auth_token` (API keys) for all devices. Device identifier is for tracking multiple installations of the same physical device.
**Clarification for Kobo Devices:**
Kobo also uses the two-field approach, but differently:
- **Kobo `device_identifier`**: User enters serial number manually (one-time registration)
- **Kobo `auth_token`**: Auto-generated API key for authentication
- **Kobo vs KOReader Difference**:
- Kobo: User manually enters serial as device_identifier
- KOReader: Plugin auto-generates UUID as device_identifier
- Both: Use auto-generated auth_token for API authentication
**Note:** This is about device MANAGEMENT, not authentication. Authentication uses `auth_token` (API keys) for all devices. Device identifier is for tracking multiple installations of the same physical device.
---
## 16.1 Codebase Investigation: device_identifier Field
@@ -753,20 +765,42 @@ X-Bookhoard-Device-ID: {device_identifier}
**Phase Integration:** Implement Device ID in Phase 1 (alongside dual authentication). It's same amount of work but solves `max_devices` problem elegantly.
**Questions for User:**
1. **Registration Workflow:** Should KOReader plugin self-register (user approves in browser) or require manual registration first?
2. **Device ID Persistence:** Where does KOReader store settings?
- `/mnt/us/koreader/settings.bookhoard.lua`?
- System `G_Settings`?
- This determines if Device ID survives plugin updates
3. **max_devices Behavior:**
- Should each Device ID count as 1 device (recommended)?
- Or limit tokens per Device ID (more complex)?
4. **Backward Compatibility:**
- How to handle existing KOReader installations without Device ID?
- Force re-registration? Auto-migrate?
5. **KOReader Plugin Repo:**
- Does `github.com/bookhoard/koreader-plugin` exist?
- Should Device ID generation happen there, or in main Bookhoard repo?
**RESOLVED**: Kobo two-field approach clarified (serial + API key)
- Kobo: Manual serial entry (device_identifier) + auto-generated API key (auth_token)
- KOReader: Auto-generated UUID (device_identifier) + auto-generated API key (auth_token)
- Documentation updated to reflect this distinction
1.**Registration Workflow:** KOReader plugin will **self-register** (user approves in browser)
- Plugin auto-generates device_identifier on first launch
- Plugin calls Bookhoard registration API with device_identifier
- User approves device in Bookhoard web UI
- Plugin receives auth_token and stores it locally
- One-time setup with approval workflow
2.**Device ID Persistence:** KOReader uses `G_reader_settings` (global settings object)
- Settings persisted to `koreader/settings.reader.lua` in the main koreader directory
- **Survives plugin updates** because settings file is separate from plugin files
- Device ID stored as `bookhoard_device_id` key
- Plugin directory can be replaced/updated without losing device identity
- Standard KOReader pattern used by Wallabag, Calibre, and other sync plugins
- Alternative considered: Plugin-specific file in `koreader/plugins/bookhoard/settings.lua` (also survives updates, but G_reader_settings is simpler)
3.**max_devices Behavior:** Each device_identifier counts as **1 device** (same as Kobo)
- Example: User has 3 Kindles with KOReader = 3 device registrations
- Reinstalling KOReader on same device reuses same device_identifier (from persisted settings)
- Reinstallation does NOT count as new device (settings file retained)
- User can regenerate auth_token without changing device_identifier
- Enforces user's max_devices limit accurately
4.**Backward Compatibility:** **Not required**
- Per @PROJECT_GUIDELINES.md: Application has never been deployed to production
- No existing KOReader installations to migrate
- Clean slate implementation - no legacy support needed
- All device registrations will use new self-registration flow from day one
5.**KOReader Plugin Repo:** Plugin does not yet exist
- Device ID generation will happen **in the plugin** (not in main Bookhoard repo)
- Plugin generates UUID on first launch and stores in local settings
- Repository to be created: `github.com/bookhoard/koreader-plugin`
- Plugin handles all KOReader-specific logic (device ID gen, API calls, UI)
- Bookhoard backend provides generic device registration endpoints only
---
@@ -953,6 +987,9 @@ This implementation plan emerged from a detailed analysis of the current authent
### Immediate Actions (When Ready to Proceed)
1. **Review this plan** - Ensure all decisions and context are captured
- **NEW**: Review updated clarification on two-field approach (device_identifier vs auth_token)
- **NEW**: Confirm understanding that Kobo requires manual serial entry (device_identifier)
- **NEW**: Confirm understanding that auth_token (API key) is auto-generated for both Kobo and KOReader
2. **Create KOReader plugin repo** - Set up `github.com/bookhoard/koreader-plugin`
3. **Begin Phase 1** - Enhanced DeviceAuthMiddleware
4. **Parallel development** - Kobo and KOReader tracks
@@ -1015,9 +1052,48 @@ Based on codebase analysis, the following documentation updates are needed:
- Shows full URL format with token
**Verification Needed:**
- Confirm lines 37-53 (device registration) don't mention entering serial number
- Verify registration flow describes automatic API key generation
- Ensure "Copy Full Sync URL" button is documented
- Confirm lines 37-53 correctly show TWO-FIELD approach:
1. **device_identifier**: Serial number (user enters manually - identifies WHICH device)
2. **auth_token**: API key (auto-generated - authenticates API requests)
- Verify registration flow explains:
- User enters serial number as device_identifier (step 1: "Find Your Kobo Serial Number")
- System generates auth_token (API key) after registration
- User copies full sync URL with auth_token (not device_identifier)
- Ensure "Copy Full Sync URL" button displays auth_token (API key), not device_identifier (serial number)
- Clarify in documentation that serial number is ONLY for device identity during registration
- Auth token (API key) is what user copies for Kobo configuration
**IMPORTANT: Two-Field Distinction for Kobo Devices**
The Kobo setup uses TWO separate database fields that serve different purposes:
1. **`device_identifier`** (Device Identity - User Entered)
- **Purpose**: Identify WHICH physical device this is
- **User Action**: Manually enter Kobo serial number (e.g., "N1234567890123")
- **Storage**: VARCHAR(255) UNIQUE NOT NULL
- **Persistence**: Remains constant for device lifetime
- **Example**: "N1234567890123"
- **Used For**: Preventing duplicate registrations, tracking device identity
2. **`auth_token`** (API Key - System Generated)
- **Purpose**: Authenticate API requests from this device
- **User Action**: Auto-generated by backend during registration, copied for configuration
- **Storage**: VARCHAR(255) UNIQUE NOT NULL
- **Persistence**: Can be revoked and regenerated without changing device_identifier
- **Example**: "dev_550e8400-e29b-41d4-a716-446655440000"
- **Used For**: Kobo sync URL, OPDS access, authentication
**Why Two Fields?**
- **Security**: `auth_token` can be revoked/regenerated if compromised
- **Flexibility**: Same physical device can get new tokens without re-registration
- **Tracking**: `device_identifier` persists across token regenerations
- **User Experience**: Register once with serial, regenerate tokens as needed
**What This Means for Documentation:**
- ✅ Step 1 (lines 37-43): Should show user finding/entering serial number (CORRECT)
- ✅ Step 2 (lines 45-72): Should show serial entered in device_identifier field (CORRECT)
- ✅ Configuration (lines 102-149): Should use auth_token in sync URL (NOT serial number)
- ✅ Sync URL format: `http://IP:8765/api/sync/kobo/{auth_token}` (serial not used)
### New Documentation: security.md
**Required Content:**
@@ -1167,8 +1243,9 @@ func (h *Handlers) RegenerateDeviceToken(c echo.Context) error {
newToken := fmt.Sprintf("dev_%s", uuid.New().String())
_, err = h.db.UpdateDevice(c.Request().Context(), database.UpdateDeviceParams{
ID: pgtype.UUID{Bytes: deviceID.Bytes(), Valid: true},
// Use new query (to be created in queries.sql)
_, err = h.db.UpdateDeviceAuthToken(c.Request().Context(), database.UpdateDeviceAuthTokenParams{
ID: pgtype.UUID{Bytes: [16]byte(deviceID), Valid: true},
AuthToken: pgtype.Text{String: newToken, Valid: true},
})
@@ -1237,22 +1314,22 @@ http://IP:8765/opds/devices/{DEVICE_ID}/catalog
**Phase 1 Tasks:**
1. **[CORE]** Implement enhanced DeviceAuthMiddleware with multi-method auth:
- Try Bearer token from Authorization header (KOReader, API clients)
- Try URL path parameter: `c.Param("token")` (Kobo sync)
- Try query parameter: `c.QueryParam("token")` (OPDS access)
- All methods lookup device via `GetDeviceByAuthToken`
- Set device context on successful auth
- Try Bearer token from Authorization header (KOReader, API clients)
- Try URL path parameter: `c.Param("token")` (Kobo sync)
- Try query parameter: `c.QueryParam("token")` (OPDS access)
- All methods lookup device via `GetDeviceByAuthToken`
- Set device context on successful auth
2. **[CORE]** Update Kobo routing to use path parameter:
- Change route from `/api/sync/kobo` to `/api/sync/kobo/:token`
- All Kobo sync endpoints inherit token from path
- Update handler to use path parameter
- Change route from `/api/sync/kobo` to `/api/sync/kobo/:token`
- All Kobo sync endpoints inherit token from path
- Update handler to use path parameter
3. **[CORE]** Add token regeneration endpoint:
- Create SQL query: `UpdateDeviceAuthToken(id, auth_token)`
- Add handler: `POST /api/devices/:id/regenerate-token`
- Generate new API key, update database
- Return new token to user
- Create new SQL query: `UpdateDeviceAuthToken(id, auth_token)` (NOT UpdateDevice - it doesn't modify auth_token)
- Add handler: `POST /api/devices/:id/regenerate-token`
- Generate new API key, update database
- Return new token to user
4. **[CRITICAL]** Update kobo-setup.md documentation:
- Remove Username/Password references (if any remain)
@@ -1313,4 +1390,20 @@ http://IP:8765/opds/devices/{DEVICE_ID}/catalog
**Plan Status**: ✅ Corrections Applied - Plan is now accurate and ready for implementation
**Summary**: Original plan was 95% solid with 3 minor inaccuracies about existing code. Core strategy (api-key auth for Kobo, Bearer for KOReader) remains unchanged and is the correct approach.
**Summary**: Original plan was 95% solid. Added critical clarifications:
1. **Two-Field Approach for Kobo**:
- `device_identifier` (serial): User enters manually, identifies WHICH device
- `auth_token` (API key): Auto-generated, authenticates API requests
- Users enter serial ONCE during registration, then use auto-generated API key for configuration
2. **Verification Step Correction**:
- Changed from: "Confirm lines 37-53 don't reference entering serial number"
- Changed to: "Confirm lines 37-53 correctly show two-field approach (serial + generated token)"
- Serial entry is CORRECT and REQUIRED for Kobo
3. **Core Strategy (Unchanged)**:
- Kobo: API key in URL path (works with stock firmware)
- KOReader: Bearer token in header (via plugin)
- Both: Use revocable `auth_token` for authentication
- OPDS: Support both methods
@@ -10,8 +10,11 @@ vars {
rating: 5
is_visible: true
library_folder: /app/uploads
opds_base_url:
}
vars:secret [
token,
refresh_token
refresh_token,
kobo_device_token,
other_device_id
]