docs: Fix device authentication implementation plan

- Update section 1.6.3 to pass baseURL as template parameter instead of hardcoding
- Add handler update note for passing cfg.BaseURL to template
- Fix TypeScript event handling in section 1.6.4:
  - Add event parameter to regenerateDeviceToken function signature
  - Update all onclick handlers to explicitly pass event object
  - Fixes deprecated implicit event in modern browsers
- Remove section 1.9 (Device Identifier Verification) as it was never implemented
- Clarify device authentication strategy: Kobo uses URL path tokens, KOReader uses Bearer headers
This commit is contained in:
2026-02-13 10:13:26 -05:00
parent fa09850611
commit fdfbec01ef
+33 -34
View File
@@ -11,15 +11,12 @@
## Table of Contents
1. [Phase 1: Enhanced Authentication (Week 1)](#phase-1-enhanced-authentication)
- Database Query Addition
- Middleware Enhancement
- Router Updates
- Backend Handler Addition
- Frontend Template Updates
- Bruno API Tests
- Database Query Addition
- Middleware Enhancement
- Router Updates
- Backend Handler Addition
- Frontend Template Updates
2. [Phase 2: Kobo Integration (Week 1-2)](#phase-2-kobo-integration)
- Documentation Updates
- Test Updates
3. [Phase 3: OPDS Security (Week 2-3)](#phase-3-opds-security)
- Router Enhancement
- Bruno API Tests
@@ -337,7 +334,7 @@ koboSync.POST("/sync-from-server", cfg.DeviceAuthMiddleware.Authenticate(koboHan
**File**: `internal/router/device.go`
**Location**: After device registration routes (around line 50)
**Location**: After device registration routes (around line 25)
**Current Implementation**: Need to check what device routes exist
@@ -350,23 +347,16 @@ koboSync.POST("/sync-from-server", cfg.DeviceAuthMiddleware.Authenticate(koboHan
devices.PUT("/:id/regenerate-token", jwtMiddleware, h.RegenerateDeviceToken)
```
**Complete Context** (assuming placement after device registration routes):
**Complete Context** (token regeneration route added to existing device routes):
```go
// Device registration endpoints
devices.POST("/register", h.InitiateRegistration)
devices.POST("/approve/:registration_id", jwtMiddleware, h.ApproveDevice)
devices.POST("/reject/:registration_id", jwtMiddleware, h.RejectDevice)
devices.GET("/pending", jwtMiddleware, h.ListPendingRegistrations)
// Existing device management routes (unchanged)
devices.GET("", jwtMiddleware, cfg.DeviceHandler.ListDevices)
devices.GET("/:id", jwtMiddleware, cfg.DeviceHandler.GetDevice)
devices.PUT("/:id", jwtMiddleware, cfg.DeviceHandler.UpdateDevice)
devices.DELETE("/:id", jwtMiddleware, cfg.DeviceHandler.DeleteDevice)
// Device management endpoints
devices.GET("", jwtMiddleware, h.ListDevices)
devices.GET("/:id", jwtMiddleware, h.GetDevice)
devices.PUT("/:id", jwtMiddleware, h.UpdateDevice)
devices.DELETE("/:id", jwtMiddleware, h.DeleteDevice)
// Token regeneration endpoint (JWT authentication required)
devices.PUT("/:id/regenerate-token", jwtMiddleware, h.RegenerateDeviceToken)
// NEW: Token regeneration endpoint (JWT authentication required)
devices.PUT("/:id/regenerate-token", jwtMiddleware, cfg.DeviceHandler.RegenerateDeviceToken)
```
**Verification**: Run `go build ./internal/router`
@@ -573,13 +563,16 @@ deviceList[i] = DeviceInfo{
**File**: `templates/devices.templ`
**Location**: Lines 46-98 (device card in grid)
**Location**: Template function signature (line 5) and device card (lines 46-98)
**Current Implementation**: Device card shows device info and settings buttons
**Required Changes**:
**Required Addition**: Add buttons for copy sync URL and regenerate token
1. **Update template signature** to accept baseURL parameter (line 5):
```templ
templ Devices(user User, devices []handlers.DeviceInfo, pendingRegistrations []PendingRegistrationData, baseURL string) {
```
**REPLACE DEVICE CARD CONTENT** (lines 46-98) with:
2. **REPLACE DEVICE CARD CONTENT** (lines 46-98) with:
```templ
for _, device := range devices {
@@ -647,12 +640,12 @@ for _, device := range devices {
type="text"
id="sync-url-{ device.ID }"
readonly
value="{ fmt.Sprintf("http://YOUR_IP:8765/api/sync/kobo/%s", device.AuthToken) }"
value="{ fmt.Sprintf("%s/api/sync/kobo/%s", baseURL, device.AuthToken) }"
class="flex-1 px-3 py-2 text-xs rounded border"
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
/>
<button
onclick="copyToClipboard('{ fmt.Sprintf("http://YOUR_IP:8765/api/sync/kobo/%s", device.AuthToken) }', 'Kobo sync URL')"
onclick="copyToClipboard('{ fmt.Sprintf("%s/api/sync/kobo/%s", baseURL, device.AuthToken) }', 'Kobo sync URL', event)"
class="px-3 py-2 text-xs rounded hover:opacity-80"
style="background-color: var(--accent); color: white;"
>
@@ -677,7 +670,7 @@ for _, device := range devices {
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border); font-family: monospace;"
/>
<button
onclick="copyToClipboard('{ device.AuthToken }', 'Auth token')"
onclick="copyToClipboard('{ device.AuthToken }', 'Auth token', event)"
class="px-3 py-2 text-xs rounded hover:opacity-80"
style="background-color: var(--accent); color: white;"
>
@@ -690,7 +683,7 @@ for _, device := range devices {
<!-- Regenerate Token Button -->
<button
onclick="regenerateDeviceToken('{ device.ID }')"
onclick="regenerateDeviceToken('{ device.ID }', event)"
class="w-full px-3 py-2 text-xs rounded border hover:opacity-80"
style="border-color: var(--border); color: var(--text-secondary); background-color: var(--bg-primary);"
>
@@ -709,6 +702,13 @@ for _, device := range devices {
- Regenerate button requires confirmation
- Clear setup instructions for each device type
**Handler Update** (`internal/router/frontend.go:171`):
```go
err = templates.Devices(user, devices, pendingList, cfg.BaseURL).Render(c.Request().Context(), &buf)
```
Pass `cfg.BaseURL` to template instead of hardcoding URLs.
#### 1.6.4 Add TypeScript Device Management
**File**: `web/src/device-management.ts` (CREATE NEW FILE)
@@ -763,7 +763,7 @@ function copyToClipboard(text: string, label: string): void {
}
// Regenerate device token with confirmation
function regenerateDeviceToken(deviceId: string): void {
function regenerateDeviceToken(deviceId: string, event: Event): void {
const confirmation = '⚠️ This will revoke current token and generate a new one.\n\n' +
'The old token will immediately stop working.\n\n' +
'You will need to update your device configuration with new token.\n\n' +
@@ -1263,7 +1263,6 @@ docs {
**KEEP EXISTING REQUESTS** in `bruno/sync-kobo/api.bru` for API clients and testing (Bearer token works for non-Kobo clients)
**Verification**: Test both authentication methods work (Kobo uses URL path, API clients can use Bearer)
---
## Phase 2: Kobo Integration