# Bookhoard Device Authentication & Sync Implementation Plan
## Executive Summary
**Current Problem:**
- OPDS routes are publicly accessible (security vulnerability)
- Kobo sync requires custom authentication approach
- KOReader documentation is incorrect about Basic Auth
- No clear authentication strategy for different device types
**Vision:**
Transform Bookhoard into a comprehensive Kindle-replacement ecosystem with seamless device sync, supporting both Kobo (native) and KOReader (via plugin) with full functionality including progress, highlights, notes, and bookmarks.
**Authentication Strategy:**
- **Kobo**: API key in URL path (per-device, revocable, no jailbreak needed)
- **KOReader**: Bearer token in Authorization header (per-device, revocable, via plugin)
- **OPDS**: Both authentication methods supported
---
## 1. Current State Analysis
### 1.1 Authentication Landscape
**Existing Infrastructure:**
```go
// DeviceAuthMiddleware - Currently only supports Bearer tokens
// File: internal/middleware/device_auth.go:37-108
func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerFunc {
// Only checks Authorization: Bearer {token}
// Validates against devices.auth_token
}
```
**Routes Using Device Auth:**
- `/api/sync/kobo/*` - Kobo sync endpoints
- `/api/sync/koreader/*` - KOReader sync endpoints
- `/opds/devices/*` - OPDS catalog
**Database Schema:**
```sql
-- devices table has auth_token field for all device authentication
-- Can store revocable API keys for Kobo devices
```
### 1.2 Kobo Integration Status
**Proven Approach (Komga):**
- Komga successfully handles Kobo sync without jailbreak using API keys in URL path
- Configuration: `api_endpoint=https://komga.example.com/kobo/{api_key}`
- Kobo firmware supports custom `api_endpoint` in `Kobo eReader.conf`
- API key embedded in URL path works reliably with stock Kobo firmware
**What's Currently Broken:**
- Current middleware expects Bearer token in Authorization header
- Routes need to support API key in URL path parameter
- Device auth tokens (API keys) already generated during registration
**What's Working:**
- Kobo sync handler implementation exists and is functional
- Endpoints registered: `/api/sync/kobo/markup`, `/bookmark`, `/v1/initialization`, etc.
- Device auth tokens (API keys) already generated during registration
- Can sync: progress, highlights, notes, bookmarks once auth is fixed
### 1.3 KOReader Integration Status
**What's Broken:**
- Documentation says use "Basic Auth" - this is WRONG
- KOReader doesn't support custom headers natively for sync
- Bearer token can't be sent via standard KOReader settings
**What's Working:**
- KOReader sync handler supports full feature set
- Can sync: progress, bookmarks, highlights, notes, library metadata
- Plugin architecture exists but no Bookhoard plugin yet
### 1.4 OPDS Status
**Current:**
- Routes registered in `internal/router/opds.go:11-12`
- ✅ **Already using DeviceAuthMiddleware** (line 12: `opds.Use(cfg.DeviceAuthMiddleware.Authenticate)`)
- ✅ Authentication is required - not publicly accessible
- Routes: `/opds/devices/:deviceId/*` - all protected
**Required:**
- ✅ No changes needed for OPDS security
- After Phase 1, OPDS will automatically support both auth methods:
- Kobo: API key in URL path (`?token=` or path parameter)
- KOReader: Bearer token via `Authorization` header
---
## 2. User Priorities (Ranked)
### Priority 1: Functionality First
**User Quote:** "I want a Kindle system replacement, otherwise I could just use Booklore or KOReader"
**Requirements:**
- ✅ Full sync: progress, highlights, notes, bookmarks
- ✅ Works on both Kobo and KOReader
- ✅ Seamless user experience
- ✅ OPDS for wireless book delivery
- ✅ Two-way sync (device ↔ server)
### Priority 2: Security Second (with documentation)
**User Quote:** "This is a self-hosted app so as long as the user is aware of the security implications in the documentation it should be fine"
**Acceptable Trade-offs:**
- API key in URL for Kobo (per-device, revocable, documented security considerations)
- Token-based for KOReader (more secure)
- Users responsible for network security (VPN, local network, HTTPS)
- Security implications clearly documented
### Priority 3: Simple Setup
**Goal:** Minimal friction for users
**Kobo Experience:**
- Register device in Bookhoard UI
- Copy device API key from device management page
- One line in `Kobo eReader.conf` with API key
- Token can be regenerated if compromised
- Works immediately with stock firmware
**KOReader Experience:**
- Copy plugin files
- Enter server URL and token
- One-time setup
- Background sync works automatically
---
## 3. Target State
### 3.1 Unified Authentication Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ DeviceAuthMiddleware (Enhanced) │
│ Unified authentication using revocable API keys: │
│ │
│ 1. Authorization: Bearer {token} │
│ → Extract from Authorization header │
│ → Lookup device by auth_token │
│ → Used by: KOReader, OPDS apps, API clients │
│ │
│ 2. URL Path Parameter: /api/sync/kobo/{token} │
│ → Extract token from URL path (c.Param("token")) │
│ → Lookup device by auth_token │
│ → Used by: Kobo e-readers (stock firmware, no jailbreak) │
│ │
│ 3. Query Parameter: ?token={token} │
│ → Extract from query string (c.QueryParam("token")) │
│ → Lookup device by auth_token │
│ → Used by: OPDS catalog access │
│ │
│ All methods result in: device context set in echo.Context │
└─────────────────────────────────────────────────────────────────┘
```
**Why This Architecture:**
- **Simple**: All methods use same auth_token lookup (no new database logic)
- **Secure**: API keys are random UUIDs, revocable, can be regenerated
- **Universal**: All devices get auth_tokens (existing field, no schema change)
- **Proven**: Komga uses this exact approach for Kobo sync successfully
- **No Firmware Limitations**: URL path parameter works with stock Kobo firmware (no jailbreak needed)
- **Revocable**: API keys can be regenerated by users without device re-registration
- **Single Authentication Method**: All device types use the same auth_token lookup, simplifying codebase
```
### 3.2 Feature Matrix
| Feature | Kobo (Stock Firmware) | KOReader (Plugin) |
|---------|---------------|-------------------|
| **Progress Sync** | ✅ Automatic (API key in URL) | ✅ Automatic (Bearer token) |
| **Highlights** | ✅ Native support | ✅ Via plugin API |
| **Bookmarks** | ✅ Native support | ✅ Via plugin API |
| **Notes** | ✅ Native support | ✅ Via plugin API |
| **OPDS** | ✅ Native support | ✅ Native OPDS support |
| **Setup Complexity** | Low (1 config line) | Medium (plugin install) |
| **Auth Method** | API key in URL path | Bearer token in header |
| **Security** | High (revocable tokens) | High (revocable tokens) |
### 3.3 User Flows
**Kobo User Journey:**
1. Open Bookhoard web UI
2. Navigate to Device Management → Add New Device
3. Enter device name and select "Kobo" as device type
4. Click Register Device
5. From device details page, copy full Kobo Sync URL with API key:
```
http://YOUR_IP:8765/api/sync/kobo/YOUR_API_KEY
```
6. Connect Kobo to computer, open `.kobo/Kobo/Kobo eReader.conf`
7. Add/edit line: `api_endpoint=http://YOUR_IP:8765/api/sync/kobo/YOUR_API_KEY`
- Paste the entire URL from step 5 (includes device API key)
8. Save and eject Kobo (restarts automatically)
9. Kobo syncs to Bookhoard instead of Kobo store
10. For OPDS: Add catalog URL with `?token=YOUR_API_KEY` parameter
**Important Note - Token Regeneration:**
- If token is compromised or lost, regenerate from device management page
- OLD token will immediately stop working
- User must update `api_endpoint` line with NEW token
- One-click copy button makes this easy
**KOReader User Journey:**
1. Install KOReader on device
2. Open Bookhoard web UI
3. Register device → copy API key
4. Approve device
5. Install Bookhoard plugin → paste API key
6. Configure sync settings
7. Read books → automatic sync
8. Add OPDS catalog with API key in header (via plugin)
---
## 4. Implementation Plan
### Phase 1: Enhanced Authentication (Week 1)
**Goal:** Make auth work for both Kobo (API key in URL) and KOReader (Bearer token)
**Tasks:**
1. **Update Routing for Kobo** (`internal/router/sync.go`)
- [ ] Change Kobo route group from `/api/sync/kobo` to `/api/sync/kobo/:token`
- [ ] Update all Kobo route handlers to use path parameter
- [ ] Test that routes still work with Bearer token (backward compatibility)
2. **Update DeviceAuthMiddleware** (`internal/middleware/device_auth.go`)
- [ ] Try Bearer token lookup first (existing behavior)
- [ ] If no Bearer token, check for `token` path parameter (`c.Param("token")`)
- [ ] Query database: `GetDeviceByAuthToken(token)` (already exists)
- [ ] Set device context on successful auth
- [ ] 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
- [ ] Return new token to user
- [ ] Require JWT auth (user must be logged in)
4. **Update Device Registration UI** (`templates/devices.templ`, `templates/partials/device-management.templ`)
- [ ] **Show "Copy Full Sync URL" button for each device** (uses clipboard API)
- [ ] For Kobo: Display complete URL: `http://IP:8765/api/sync/kobo/{DEVICE_TOKEN}`
- [ ] **Add "Regenerate Token" button** (with confirmation dialog: "This will revoke current token. Continue?")
- [ ] After regeneration, show toast: "Token regenerated - update Kobo config"
- [ ] For KOReader: Show auth token in copyable input field
- [ ] Display clear setup instructions per device type with full URL example
5. **Apply Middleware to OPDS** (`internal/router/opds.go`)
- [ ] Verify OPDS routes have DeviceAuthMiddleware (already applied)
- [ ] Add support for `?token=` query parameter for OPDS access
- [ ] Update comments to reflect dual auth support
**Testing:**
- Kobo device sync with api-key url auth
- KOReader sync with Bearer token auth
- OPDS access with both auth methods
---
### Phase 2: Kobo Integration (Week 1-2)
**Goal:** Seamless Kobo experience with API key-based auth (stock firmware)
**Tasks:**
1. **Kobo Setup Documentation** (`docs/user/devices/kobo-setup.md`)
- [ ] Show how to get API key from Bookhoard device management page
- [ ] Step-by-step guide for editing `Kobo eReader.conf`
- [ ] Include example config: `api_endpoint=http://192.168.1.100:8765/api/sync/kobo/{API_KEY}`
- [ ] Security warning about API keys in logs
- [ ] Network security recommendations (VPN, local network)
- [ ] Instructions for regenerating token if compromised
- **Note**: Documentation was updated on 2026-02-12 to reflect API key authentication
- **Verification**: Confirm lines 37-53 don't reference entering serial number (device registration generates token automatically)
2. **Kobo Sync Validation** (`cmd/server/tests/kobo_test.go`)
- [ ] Update tests to use API key in URL path
- [ ] Test sync endpoints with `/api/sync/kobo/{token}/markup` format
- [ ] Verify all features work: progress, highlights, bookmarks, notes
- [ ] Test token regeneration doesn't break existing sync
3. **Kobo OPDS Integration**
- [ ] Document OPDS URL format: `http://IP:8765/opds/devices/{DEVICE_ID}/catalog?token={API_KEY}`
- [ ] Test OPDS browsing with API key in URL parameter
- [ ] Test book downloads
**Success Criteria:**
- New Kobo device registered and syncing within 5 minutes
- All sync features working without jailbreak
- API key can be regenerated if compromised
- Security implications clearly documented
- Works with stock Kobo firmware (no modifications)
---
### Phase 3: KOReader Plugin Development (Week 2-4)
**Goal:** Full-featured KOReader plugin with Bearer token auth
**Tasks:**
1. **Plugin Architecture**
- [ ] Create `koreader-plugin/bookhoard.koplugin/` directory
- [ ] Implement `_meta.lua` (plugin metadata)
- [ ] Implement `main.lua` (entry point)
2. **Core Plugin Features**
- [ ] Settings UI: server URL, auth token, sync options
- [ ] HTTP client with Bearer token authentication
- [ ] Auto-sync on events:
- Page turn (configurable: every N pages)
- Bookmark added/deleted
- Highlight created/deleted
- Note added/edited
- [ ] Manual sync button in KOReader menu
- [ ] Sync status indicator
- [ ] Error handling and retry logic
3. **Plugin API Integration**
- [ ] Call `/api/sync/koreader/progress` with reading position
- [ ] Call `/api/sync/koreader/bookmarks` with annotations
- [ ] Call `/api/sync/koreader/metadata` to get server-side progress
- [ ] Handle conflicts (server vs local progress)
4. **Plugin Documentation**
- [ ] Installation guide (copy files, restart KOReader)
- [ ] Configuration guide (get token from Bookhoard UI)
- [ ] Usage instructions
- [ ] Troubleshooting guide
**Success Criteria:**
- Plugin installed and configured in under 10 minutes
- Background sync works transparently
- All annotation types sync properly
- No Lua errors or crashes
---
### Phase 4: OPDS Security & Integration (Week 2-3)
**Goal:** Secure OPDS with unified device authentication
**Tasks:**
1. **Secure OPDS Routes** (`internal/router/opds.go`)
- [ ] Apply `DeviceAuthMiddleware` to all OPDS endpoints
- [ ] Ensure auth works with both api-key and Bearer methods
- [ ] Update router comments
2. **Kobo OPDS Access**
- [ ] Document OPDS URL with device ID
- [ ] Test OPDS access via Kobo browser
- [ ] Test book downloads
3. **KOReader OPDS Access**
- [ ] Works natively via KOReader's OPDS support
- [ ] Uses Bearer token in header
- [ ] Document catalog URL format
4. **Test Coverage** (`cmd/server/tests/opds_test.go`)
- [ ] Test unauthenticated access → expect 401
- [ ] Test with valid Bearer token → expect 200
- [ ] Test with valid api-key url → expect 200
- [ ] Test book download with auth
---
### Phase 5: Testing & Documentation (Week 4)
**Goal:** Production-ready release with comprehensive docs
**Tasks:**
1. **End-to-End Testing**
- [ ] Fresh Kobo setup: register → sync → highlights → OPDS
- [ ] Fresh KOReader setup: install plugin → sync → highlights → OPDS
- [ ] Cross-device sync: Kobo ↔ KOReader same book
- [ ] Offline scenarios and conflict resolution
- [ ] Error handling and recovery
2. **Security Documentation**
- [ ] Security implications page
- [ ] Network security recommendations
- [ ] Comparison: API Key (URL path) vs Bearer token - both use revocable auth_token
- [ ] Threat model for self-hosted users
3. **User Documentation**
- [ ] Quick start guide (Kobo)
- [ ] Quick start guide (KOReader)
- [ ] Feature comparison table
- [ ] FAQ and troubleshooting
4. **Release Preparation**
- [ ] Version bump
- [ ] Changelog
- [ ] Migration guide (if needed)
---
## 5. Technical Decisions
### 5.1 Why API Key in URL Path for Kobo?
**Approach Chosen:** Per-device API key embedded in URL path: `/api/sync/kobo/{api_key}/...`
**Pros:**
- No jailbreak needed - works with stock Kobo firmware
- Proven approach - Komga uses this successfully
- API keys are random UUIDs (cryptographically secure)
- Tokens can be revoked and regenerated
- Single configuration line in Kobo config
- Simple token management (user can regenerate)
- Uses existing `auth_token` field (no schema change)
**Cons:**
- API key visible in Kobo logs and server logs
- URL-based authentication (less secure than headers)
- Key appears in Kobo config file (plain text)
**Mitigations:**
- Use HTTPS for sync (reverse proxy)
- Document that API keys are sensitive
- Recommend local network only (VPN for remote)
- Provide one-click token regeneration
- Acceptable security for self-hosted use (like Komga)
**Alternative Considered:**
- **Serial-based auth**: Rejected because it uses predictable serials that can't be revoked and is less secure
- **Kobo store token**: Rejected because it's managed by Kobo, not under our control
- **Custom HTTP headers**: Rejected because Kobo firmware doesn't support custom headers
**Authentication Strategy:**
- Kobo: API key in URL path (per-device, revocable)
- KOReader: Bearer token (per-device, revocable)
- OPDS: Both methods supported (API key via query parameter, bearer token via header)
### 5.2 Why Not Use Kobo's Token?
Kobo sends `Authorization: Bearer {kobo-store-token}` which is:
- Managed by Kobo (not under our control)
- May rotate/expire without warning
- Only valid for Kobo's servers
- Doesn't identify device in our system
- Can't be extracted from Kobo config without jailbreak
**Decision:** Use API key in URL path instead (proven by Komga). The API key in URL path provides per-device authentication that is revocable and works with stock Kobo firmware without jailbreak.
### 5.3 Token Regeneration Strategy
**Backend:**
- Endpoint: `POST /api/devices/:id/regenerate-token`
- Requires: User JWT auth (device owner)
- Action: Generate new UUID, update `devices.auth_token`
- Response: Return new token to user
**Frontend:**
- "Regenerate Token" button on device management page
- Confirmation dialog: "This will revoke current token. Continue?"
- One-click copy to clipboard
- Update display instructions with new token
- Toast notification: "Token regenerated successfully"
**Impact:**
- Old token immediately invalid
- Device must update config with new token
- Sync will fail until config updated
- Useful for: Lost tokens, compromised devices, periodic rotation
### 5.4 Why Bearer Token for KOReader?
**Pros:**
- Cryptographically secure (random UUID)
- Can be revoked/regenerated
- Standard authentication method
- Works with KOReader plugin architecture
- Plugin can store token securely
**Cons:**
- Requires plugin (can't use native settings)
- Token must be copy-pasted during setup
- User must manage token
**Trade-off Accepted:**
More secure but requires plugin installation. Justified because KOReader users are typically more technical and willing to install plugins.
**Note:** Same token regeneration as Kobo (reuses backend logic)
---
## 6. Files to Modify
### Core Authentication
- `internal/middleware/device_auth.go` - Add URL path token extraction
- `internal/router/sync.go` - Update Kobo routes to use path parameter
- `internal/router/opds.go` - Add ?token= query parameter support (already has middleware)
### Device Management
- `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
- Create `templates/devices-kobo-config.templ` - Kobo config instructions with API key
### Testing
- `cmd/server/tests/kobo_test.go` - Update for URL path token
- `cmd/server/tests/opds_test.go` - Add ?token= auth tests
- `cmd/server/tests/test_helpers.go` - Add token regeneration helpers
### Documentation
- `docs/user/devices/kobo-setup.md` - Rewrite for API key auth
- `docs/user/devices/koreader-setup.md` - Update for Bearer token
- `docs/user/devices/security.md` - New security considerations doc
- `docs/developer/api/kobo/` - Update API docs with path parameters
### New Files
- `koreader-plugin/bookhoard.koplugin/_meta.lua` - Plugin metadata (Phase 3)
- `koreader-plugin/bookhoard.koplugin/main.lua` - Plugin implementation (Phase 3)
- `docs/user/devices/security.md` - Security considerations
---
## 7. Success Metrics
**Functionality:**
- ✅ Kobo syncs progress, highlights, notes, bookmarks automatically
- ✅ KOReader syncs same features via plugin
- ✅ OPDS works on both platforms
- ✅ Cross-device sync works (read on Kobo, continue on KOReader)
**Usability:**
- ✅ Kobo setup time: < 5 minutes
- ✅ KOReader setup time: < 10 minutes
- ✅ No token management for Kobo users
- ✅ Clear documentation with security warnings
**Security:**
- ✅ OPDS no longer publicly accessible
- ✅ Auth required for all sync endpoints
- ✅ Security implications documented
- ✅ Network security recommendations provided
---
## 8. Future Enhancements (Out of Scope)
- Auto-discovery of devices on network
- QR code setup for mobile devices
- Background sync service (no plugin needed)
- Readwise/Joplin integration
- Calibre integration
- Mobile apps (iOS/Android)
---
## 9. Decisions Made (User Input)
Based on user feedback, the following decisions have been finalized:
### 9.1 KOReader Sync Trigger
**Decision:** Configurable with multiple options
- **Auto-sync**: Every N pages (configurable, default every 10 pages)
- **Auto-sync**: Every N minutes (configurable, default every 5 minutes)
- **Manual sync**: Always available via menu
- **Event-triggered**: On bookmark/highlight/note creation (immediate)
**Rationale:** User wants automatic sync for convenience but manual fallback for control.
### 9.2 Plugin Distribution
**Decision:** Separate repository under Bookhoard organization
- Repository: `github.com/bookhoard/koreader-plugin`
- Licensed under same terms as Bookhoard
- Versioned independently
- Referenced in main Bookhoard documentation
**Rationale:** Clean separation of concerns, easier plugin-specific issues/PRs.
### 9.3 Kobo Authentication
**Decision:** API key in URL path (per-device)
- No serial number needed
- Token entered in device registration (already exists)
- Configured in Kobo as: `api_endpoint=http://IP:8765/api/sync/kobo/{TOKEN}`
- Token can be regenerated by user (new endpoint)
- Stored in `devices.auth_token` field (no schema change)
**Rationale:** Simplest user experience (just copy token), most secure (revocable), proven to work (Komga).
### 9.4 Conflict Resolution
**Decision:** Use existing codebase strategy
- Already implemented in `internal/handlers/koreader.go`
- Uses timestamp-based last-write-wins
- Device priority can be configured per-device
**Rationale:** Don't reinvent the wheel, existing implementation is sufficient.
### 9.5 Implementation Order
**Decision:** Parallel implementation with separate sections
- Phase 2a: Kobo Integration
- Phase 2b: KOReader Plugin (runs concurrently)
- Separate milestones and deliverables
- Can ship Kobo support before plugin is ready
**Rationale:** Faster time-to-market for Kobo users, plugin can follow.
### 9.6 KOReader Device Identification Strategy
**Problem:** Multiple KOReader installations each register as separate devices, exhausting `max_devices` limit even though they represent the same physical device.
**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
**Investigation Date:** 2026-02-12
**Purpose:** Clarify the role and usage of `device_identifier` field in the devices table
### Git History Analysis
**Introduction (Commit 3b2075f, Phase 1 - "highlights and notes annotation system"):**
- Added as part of broader annotation system feature
- Original intent: Track physical device identity across software reinstalls
- Has existed since early project history (not legacy code)
### Current Codebase Usage
**Active Usage:**
```go
// internal/handlers/devices.go:37
DeviceIdentifier string `json:"device_identifier" validate:"required,min=1,max=255"`
```
- Required field in device registration requests
- Stored in `devices.device_identifier` (VARCHAR(255) UNIQUE NOT NULL)
- Used in registration flow to track device identity
**Dead Code:**
```sql
-- internal/database/queries/queries.sql:731
-- name: GetDeviceByIdentifier :one
SELECT * FROM devices WHERE device_identifier = $1;
```
- Function exists in generated database code (`internal/database/querier.go:130`)
- **NOT called** anywhere in handlers or tests (0 references)
- Can be considered for removal during code cleanup
### OPDS Handler Behavior
**Current Implementation (internal/handlers/opds.go:56-89):**
```go
deviceID := c.Param("deviceId") // Extracts UUID from URL
deviceUUID, err := uuid.Parse(deviceID)
device, err := h.db.GetDevice(c.Request().Context(), pgtype.UUID{Bytes: deviceUUID, Valid: true})
```
**Finding:** OPDS handler uses device `id` (UUID) for lookup, NOT `device_identifier`
### Authentication vs Device Identification
**Authentication (what currently works):**
- `auth_token` field stores revocable API keys
- Used by `DeviceAuthMiddleware` for all device authentication
- Bearer token or URL path parameter lookup via `GetDeviceByAuthToken`
**Device Identification (what this is about):**
- `device_identifier` field tracks physical device identity
- Helps prevent duplicate device registrations for same physical device
- NOT used for authentication (only for device management/registration)
### Conclusion
**No inconsistency found.** The implementation plan's handling of `device_identifier` is accurate:
- Field exists for device management purposes
- Required in current registration flow
- Auth uses `auth_token` only (no confusion)
- Dead query (`GetDeviceByIdentifier`) can be removed during cleanup
**Decision:** **Keep Section 9.6** - The KOReader Device Identification Strategy is valid for solving the `max_devices` exhaustion problem. The plan correctly distinguishes between device identification (management) and authentication (security).
---
**Architecture:**
- Plugin generates unique `device_id` on first launch (stored in KOReader settings)
- Plugin sends `device_identifier` during registration API call
- Backend validates `auth_token` matches device record
- Reinstalling KOReader reads same `device_id` from settings, reuses existing device record
**Registration Flow Options:**
| Approach | User Experience | Complexity | Pros/Cons |
|----------|-----------------|------------|-------------|
| **A. Manual Link** | User registers in Bookhoard, copies Device ID + Token | Low | ✅ Simple server
❌ User copies two values |
| **B. Self-Register** | Plugin auto-registers, user approves in web UI | Medium | ✅ One-time setup
⚠️ Requires approval endpoint |
| **C. QR Code Bridge** | Scan QR code with Device ID + Token | Low-Medium | ✅ Very user-friendly
❌ Requires QR library |
**Recommendation:** **Option B (Self-Register)** for best UX.
**Backend Changes Required:**
```go
// Devices table - Add Device Identifier (Already Exists!)
-- devices.device_identifier already exists (models.go:67)
-- No schema change needed - just use it properly
// internal/handlers/devices.go - InitiateDeviceRegistration()
// Add optional pre_generated_device_id parameter:
DeviceIdentifier string `json:"device_identifier" validate:"omitempty,uuid"`
```
**Plugin Changes Required:**
```lua
-- First Run Detection:
-- On first launch:
if not G_Settings:hasSetting("bookhoard_device_id") then
local device_id = uuid.generate()
G_Settings:saveSetting("bookhoard_device_id", device_id)
G_Settings:saveSetting("bookhoard_device_name", "KOReader on " .. Device.model)
-- Trigger registration
register_device()
end
-- Registration API Call:
-- POST /api/devices/register
{
device_name: "KOReader on Kindle Paperwhite",
device_type: "koreader",
device_identifier: "550e8400-e29b..." -- From plugin settings
}
```
**Device Management UI:**
- Display `device_identifier` for each device
- "Copy Device ID" button for KOReader setup
- "Regenerate Token" doesn't change Device ID
- Show "Linked Devices" count (e.g., "3 KOReader installations linked")
**Sync Flow With Device ID:**
```lua
-- Plugin Headers:
Authorization: Bearer {auth_token}
X-Bookhoard-Device-ID: {device_identifier}
```
**Benefits:**
| Aspect | Current | With Device ID |
|---------|----------|---------------|
| **Reinstalls** | New device each time | Same device, update token only |
| **max_devices** | Counts installs, not devices | Accurate device count |
| **Security** | Token-only | Device ID + token (two-factor) |
| **Troubleshooting** | "Which KOReader is this?" | Clear device identification |
| **Plugin Transfer** | Manual token copy | Migrate settings file |
**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:**
✅ **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
---
## 10. Historical Context & Conversation Summary
### Why This Plan Exists
This implementation plan emerged from a detailed analysis of the current authentication and sync architecture. Key discoveries from codebase review:
#### The Authentication Problem
**Original Issue:** The plan started with `@fix-opds-device-authentication.md` which identified:
- OPDS routes were publicly accessible (security vulnerability)
- Test `GetDeviceCatalog_WithoutDeviceAuth` expected 401 but got 404
- DeviceAuthMiddleware was already applied to OPDS routes, but tests were failing
**Root Cause Discovery:**
- Kobo devices send `Authorization: Bearer {kobo-store-token}` (Kobo's store token, not Bookhoard's)
- Kobo cannot send custom Bearer tokens through `eReader.conf`
- Current middleware only supports Bearer tokens in Authorization header
- Solution: Use API key in URL path (proven by Komga)
#### The Sidecar File Red Herring
**Initial Thought:** Use `.bookhoard.json` sidecar file to pass device tokens to Kobo
**Problem Discovered:**
- Sidecar handler exists (`internal/handlers/sidecar.go`) but routes are NOT registered
- Kobo firmware is locked down - cannot read custom config files
- No plugin architecture on Kobo (unlike KOReader)
- Would require Kobo firmware modification (impossible for most users)
**Decision:** Abandon sidecar approach, use API key in URL path (proven by Komga)
- API keys are revocable and regenerable
- Works with stock Kobo firmware
- Simple user configuration
#### KOReader Documentation Bug
**Original Documentation:** Claimed KOReader uses "Basic Auth" with username/password
**Reality:**
- KOReader sync sends `Authorization: Bearer {token}` to sync servers
- The official KOReader sync protocol uses MD5 hashed passwords
- Bookhoard's implementation expects Bearer tokens in `DeviceAuthMiddleware`
- KOReader cannot send custom headers through native settings
**Solution:** Create KOReader plugin that can send proper Bearer tokens
#### Booklore Analysis
**Investigation:** Analyzed how Booklore handles Kobo authentication
**Booklore Approach:**
- Token embedded in URL path: `/api/kobo/{token}/...`
- Works but token appears in logs
- One token per user (not per device)
- OPDS uses separate Basic Auth
**Decision:** Don't copy Booklore - less secure. Use device-specific auth with proper tokens for KOReader.
#### Plugin Feasibility Research
**Concern:** Is KOReader a "moving target" for plugin development?
**Findings:**
- UI plugins are unstable (Issue #13942) - frequent breaking changes
- Backend/sync plugins (like Wallabag) are stable
- Wallabag2 plugin has minimal updates over years
- HTTP client APIs don't change often
**Decision:** Proceed with plugin - sync plugins are low-risk
### Technical Constraints Discovered
1. **Kobo Firmware Limitations:**
- Cannot send custom HTTP headers
- Cannot install plugins
- Settings in `eReader.conf` are limited to specific keys
- Solution: API key in URL path works with stock firmware
2. **KOReader Capabilities:**
- Can install plugins (copy files to `/koreader/plugins/`)
- Lua-based plugin architecture
- Can send custom HTTP requests
- Plugin can register itself
- Native OPDS client support
3. **Security Trade-offs:**
- API Key in URl: Which means it's stored in logs
- Bearer tokens: Cryptographically secure, requires setup
- User accepted security/usability trade-off for self-hosted use
### Why Functionality Trumps Security
**User Quote:** "I want a Kindle system replacement, otherwise I could just use booklore or KOReader. My biggest concern is do any of these limit any of the syncing/downloading implementation I already have. Functionality and features are more important than anything else. Although security does come in at a close second."
**Translation:**
- Self-hosted = trusted network environment
- User understands and accepts risks
- Willing to document security implications
- Prioritizes working features over perfect security
**Resulting Architecture:**
- Kobo: API key in URL path (revocable, simple)
- KOReader: Bearer token (high security, requires plugin setup)
- Both get full feature parity
- Security implications clearly documented
---
## 11. Updated Implementation Timeline
### Parallel Track Structure
**Track A: Kobo Integration**
- Phase 1: Enhanced Auth (Week 1)
- Phase 2a: Kobo Implementation (Week 1-2)
- Phase 4: Kobo Testing (Week 3)
**Track B: KOReader Plugin**
- Phase 1: Enhanced Auth (Week 1) [shared]
- Phase 2b: Plugin Development (Week 2-4)
- Phase 4: Plugin Testing (Week 4)
**Track C: OPDS Security**
- Phase 3: OPDS Integration (Week 2-3)
**Track D: Documentation**
- Phase 5: Final Docs & Release (Week 4)
### Milestones
**Milestone 1 (End of Week 2):** Kobo Fully Functional
- Kobo: API key in URL (works with stock firmware)
- All sync features operational
- OPDS access secured
- Can ship to Kobo users
**Milestone 2 (End of Week 4):** KOReader Plugin Ready
- Plugin released in separate repo
- Full feature parity with Kobo
- Documentation complete
- Production release
---
## 12. Files & Components Reference
### Critical Files for Implementation
**Authentication Core:**
- `internal/middleware/device_auth.go:37-108` - Main auth middleware
- `internal/database/queries/queries.sql` - Device queries
- `internal/database/models.go` - Device model (has device_identifier)
**Routing:**
- `internal/router/opds.go:11-12` - OPDS routes (need middleware)
- `internal/router/sync.go:17-50` - Sync routes
- `internal/router/device.go` - Device management routes
**Handlers:**
- `internal/handlers/kobo.go:26-679` - Kobo sync implementation
- `internal/handlers/koreader.go:23-897` - KOReader sync implementation
- `internal/handlers/opds.go` - OPDS handler
**Tests:**
- `cmd/server/tests/kobo_test.go` - Kobo tests
- `cmd/server/tests/opds_test.go` - OPDS tests
- `cmd/server/tests/test_helpers.go` - Test utilities
**Documentation (to update):**
- `docs/user/devices/kobo-setup.md` - Currently has wrong info
- `docs/user/devices/koreader-setup.md` - Needs Bearer token info
**Sidecar (reference only, not implementing):**
- `internal/handlers/sidecar.go` - Exists but not wired up
- Routes not registered in router
---
## 13. Next Steps
### 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
### Success Criteria (Reiterated)
**Functional:**
- ✅ Kobo: Enter serial → register → sync works immediately
- ✅ KOReader: Install plugin → plugin registers → sync works
- ✅ Both: Full sync (progress, highlights, notes, bookmarks)
- ✅ OPDS: Authenticated access on both platforms
- ✅ Cross-device: Read on one, continue on another
**Security:**
- ✅ OPDS no longer publicly accessible (authenticated with API key or Bearer token)
- ✅ All sync endpoints require device authentication (API key or bearer token)
- ✅ Device authentication uses revocable API keys (Kobo/KOReader)
- ✅ Security implications documented
- ✅ Network security recommendations provided
**User Experience:**
- ✅ Kobo setup: < 5 minutes
- ✅ KOReader setup: < 10 minutes
- ✅ Clear documentation
- ✅ Working examples
---
**Plan Status:** Ready for implementation
**Last Updated:** Based on conversation ending with user decisions
**Note:** User indicated it's late and not proceeding tonight
**Ready to implement when you are. This plan captures all our discussion and decisions.**
---
## 16. Documentation Updates Required (Pre-Implementation Checklist)
Based on codebase analysis, the following documentation updates are needed:
### koreader-setup.md (docs/user/devices/koreader-setup.md)
**Current Issues:**
- Line 126: Says "Basic Auth" - should be "Bearer Token"
- Lines 127-128: Reference username/password - should reference auth_token
- Documentation states KOReader uses Basic Auth (incorrect)
**Required Changes:**
```diff
- 1. **Authentication Method**: Select "Basic Auth"
- 2. **Username**: Your Bookhoard email or username
- 3. **Password**: Your Bookhoard password
+ 1. **Authentication Method**: Bearer Token (API Key)
+ 2. **Auth Token**: Copy from Bookhoard Device Management page
+ 3. **Setup**: Plugin will include token in Authorization header automatically
```
### kobo-setup.md (docs/user/devices/kobo-setup.md)
**Current Status:**
- Updated on 2026-02-12 to reflect API key authentication
- Shows full URL format with token
**Verification Needed:**
- 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:**
- API key in URL path security considerations
- Comparison: API key (URL) vs Bearer token (header)
- Network security recommendations (HTTPS, VPN, local-only)
- Token regeneration best practices
- Risk mitigations for self-hosted deployments
---
## 10. Risk Assessment
**Low Risk:**
- API key in URL path (well-understood pattern)
- OPDS security (straightforward middleware application)
- Kobo sync (already implemented, just fixing auth route)
**Medium Risk:**
- KOReader plugin development (Lua learning curve, potential API changes)
- Cross-device sync conflicts (need clear resolution strategy)
**High Risk:**
- KOReader API instability (if they change plugin APIs frequently)
- User adoption (may resist plugin installation)
**Mitigations:**
- Start with Kobo to prove concept
- Simple plugin architecture (minimize breaking changes impact)
- Excellent documentation to reduce friction
- Community feedback loop
---
## 11. Timeline Summary
| Phase | Duration | Key Deliverable |
|-------|----------|----------------|
| Phase 1 | Week 1 | Enhanced auth middleware working |
| Phase 2 | Week 1-2 | Kobo fully functional |
| Phase 3 | Week 2-4 | KOReader plugin complete |
| Phase 4 | Week 2-3 | OPDS secured |
| Phase 5 | Week 4 | Testing & docs complete |
**Total Duration: 4 weeks**
---
**Next Steps:**
1. User reviews and approves plan
2. Answer open questions (Section 9)
3. Begin Phase 1 implementation
4. Weekly check-ins on progress
**Ready to proceed?**
---
## 14. Authentication Strategy Confirmation
### 14.1 Kobo Authentication: API Key in URL Path
**Decision:** Use per-device API keys embedded in URL path (proven by Komga)
**Architecture:**
- Kobo route: `/api/sync/kobo/{api_key}/*`
- API key generated during device registration
- User copies full URL from device management page
- Paste into Kobo's `api_endpoint` configuration
**Why This Approach:**
- ✅ Proven by Komga (production-tested, works reliably)
- ✅ No jailbreak needed - works with stock Kobo firmware
- ✅ API keys are revocable and can be regenerated
- ✅ Uses existing `auth_token` field (no schema change)
- ✅ Simple user configuration (one line in config file)
- ✅ More secure than serial-based authentication
**Rejection: Serial-based via X-Kobo-Device Header**
- Serials are predictable and cannot be revoked
- More complex implementation (JSON parsing, device_identifier lookup)
- Less secure than random API keys
- No benefit over URL path approach
---
### 14.2 Router Configuration
**Required Change:**
```go
// Current routing
koboSync := e.Group("/api/sync/kobo")
koboSync.POST("/markup", cfg.DeviceAuthMiddleware.Authenticate(...))
// New routing (supports API key in URL path)
koboSync := e.Group("/api/sync/kobo/:token")
koboSync.POST("/markup", cfg.DeviceAuthMiddleware.Authenticate(...))
```
**Middleware Enhancement:**
```go
// Add path parameter extraction (supports both Bearer token and URL token)
func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerFunc {
// 1. Try Bearer token header (KOReader, API clients)
authHeader := c.Request().Header.Get("Authorization")
if authHeader != "" {
token := strings.TrimPrefix(authHeader, "Bearer ")
device, err := m.db.GetDeviceByAuthToken(c.Request().Context(), token)
if err == nil {
return m.setDeviceContext(c, device)
}
}
// 2. Try URL path parameter (Kobo, OPDS)
urlToken := c.Param("token")
if urlToken == "" {
urlToken = c.QueryParam("token") // Fallback to query param
}
if urlToken != "" {
device, err := m.db.GetDeviceByAuthToken(c.Request().Context(), urlToken)
if err == nil {
return m.setDeviceContext(c, device)
}
}
return c.JSON(401, map[string]string{"error": "authentication required"})
}
```
**Frontend:**
- Display `auth_token` for each device (API key for Kobo, token for KOReader)
- "Copy Device ID" button for KOReader setup
- "Regenerate Token" doesn't change auth_token
---
### 14.3 Token Regeneration
**Required Addition:** Backend endpoint to regenerate tokens
**Implementation:**
```go
// internal/handlers/devices.go
func (h *Handlers) RegenerateDeviceToken(c echo.Context) error {
deviceID, err := uuid.Parse(c.Param("id"))
// ... user auth check ...
newToken := fmt.Sprintf("dev_%s", uuid.New().String())
// 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},
})
return c.JSON(200, map[string]string{
"auth_token": newToken,
"message": "Token regenerated successfully",
})
}
```
**Route:**
```go
// internal/router/device.go
devices.PUT("/:id/regenerate-token", jwtMiddleware, h.RegenerateDeviceToken)
```
**Frontend:**
- "Regenerate Token" button on device details
- Confirmation dialog
- One-click copy to clipboard
- Update Kobo config example with new token
**Status**: ✅ New feature needed
---
### 14.4 Enhanced Security Documentation
**Add to `docs/user/security.md`** (new file):
## API Key Authentication Security Considerations
### Risks
- **URL Exposure**: API keys visible in Kobo logs and server logs
- **Config File Storage**: API key appears in Kobo config file (plain text)
- **Network Interception**: On public networks, API key in URL could be intercepted
- **Device Theft**: Physical access to device grants access until token is regenerated
### Recommended Mitigations
1. **Network Security** (REQUIRED for self-hosted app)
- Use HTTPS for sync (reverse proxy with SSL/TLS)
- If you expose Bookhoard publicly: Use reverse proxy with SSL/TLS termination
- Ensure API keys are transmitted over encrypted connection
- Keep firewall rules restricting access by IP (fail2ban for repeated failed auth attempts)
2. **Token Management**
- Regenerate API keys if compromised or lost
- One-click regeneration in device management UI
- Automatic expiration and rotation (optional, for high-security deployments)
3. **Security Documentation**
- Document that API keys are sensitive (like passwords)
- Recommend local network only (home or VPN)
- For remote access, use reverse proxy with valid SSL certificates
- Provide clear warnings in UI when API keys are displayed or copied
# Kobo OPDS
http://IP:8765/opds/devices/{DEVICE_ID}/catalog?token={API_KEY}
# KOReader OPDS (via plugin - uses header automatically)
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
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
3. **[CORE]** Add token regeneration endpoint:
- 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)
- Verify registration flow reflects API key generation:
- Device registration automatically generates `auth_token`
- User copies full sync URL from device management page (not just token)
- Example: `http://IP:8765/api/sync/kobo/{API_KEY}`
- Explain API key in URL configuration
- Show full URL with token: `http://IP:8765/api/sync/kobo/{API_KEY}`
- Document token regeneration process
- **Status**: Documentation was updated on 2026-02-12 to reflect API key auth
- **Verification Needed**: Ensure no references to entering serial number remain (lines 37-53)
5. **[CRITICAL]** Update koreader-setup.md documentation:
- Change "Basic Auth" to "Bearer Token" authentication (line 126)
- Remove incorrect username/password references (lines 127-128)
- Currently: "Username: Your Bookhoard email or username"
- Currently: "Password: Your Bookhoard password"
- Should be: "Auth Token: Your device API key from Bookhoard"
- Explain plugin token management
- **Note**: Current docs mention "Basic Auth" which is INCORRECT
- **Note**: KOReader uses Bearer token in Authorization header (not Basic Auth)
6. **[TESTING]** Update test coverage:
- Test Kobo sync with API key in URL path
- Test KOReader sync with Bearer token header
- Test OPDS access with both auth methods (path param and query param)
- Test token regeneration invalidates old token
---
## 15. Updated Success Criteria
**Authentication:**
- ✅ Kobo API key auth working via URL path parameter
- ✅ KOReader Bearer token auth working via Authorization header
- ✅ OPDS accessible via BOTH authentication methods (middleware handles both)
- ✅ Token regeneration endpoint functional
- ✅ Security implications documented with mitigation strategies
**Database:**
- ✅ `auth_token` field used for all devices (no schema change)
- ✅ Token regeneration updates existing field
- ✅ `device_identifier` column available for KOReader device linking (not used for auth)
**Frontend:**
- ✅ Device management UI has copy/regenerate token buttons
- ✅ Kobo config shows API key in URL format
- ✅ Clear setup instructions per device type
**Documentation:**
- ✅ kobo-setup.md reflects API key authentication
- ✅ koreader-setup.md updated for Bearer token
- ✅ security.md created with token management considerations
- ✅ OPDS authentication clearly documented
---
**Plan Status**: ✅ Corrections Applied - Plan is now accurate and ready for implementation
**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