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:
+33
-34
@@ -11,15 +11,12 @@
|
|||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
1. [Phase 1: Enhanced Authentication (Week 1)](#phase-1-enhanced-authentication)
|
1. [Phase 1: Enhanced Authentication (Week 1)](#phase-1-enhanced-authentication)
|
||||||
- Database Query Addition
|
- Database Query Addition
|
||||||
- Middleware Enhancement
|
- Middleware Enhancement
|
||||||
- Router Updates
|
- Router Updates
|
||||||
- Backend Handler Addition
|
- Backend Handler Addition
|
||||||
- Frontend Template Updates
|
- Frontend Template Updates
|
||||||
- Bruno API Tests
|
|
||||||
2. [Phase 2: Kobo Integration (Week 1-2)](#phase-2-kobo-integration)
|
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)
|
3. [Phase 3: OPDS Security (Week 2-3)](#phase-3-opds-security)
|
||||||
- Router Enhancement
|
- Router Enhancement
|
||||||
- Bruno API Tests
|
- Bruno API Tests
|
||||||
@@ -337,7 +334,7 @@ koboSync.POST("/sync-from-server", cfg.DeviceAuthMiddleware.Authenticate(koboHan
|
|||||||
|
|
||||||
**File**: `internal/router/device.go`
|
**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
|
**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)
|
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
|
```go
|
||||||
// Device registration endpoints
|
// Existing device management routes (unchanged)
|
||||||
devices.POST("/register", h.InitiateRegistration)
|
devices.GET("", jwtMiddleware, cfg.DeviceHandler.ListDevices)
|
||||||
devices.POST("/approve/:registration_id", jwtMiddleware, h.ApproveDevice)
|
devices.GET("/:id", jwtMiddleware, cfg.DeviceHandler.GetDevice)
|
||||||
devices.POST("/reject/:registration_id", jwtMiddleware, h.RejectDevice)
|
devices.PUT("/:id", jwtMiddleware, cfg.DeviceHandler.UpdateDevice)
|
||||||
devices.GET("/pending", jwtMiddleware, h.ListPendingRegistrations)
|
devices.DELETE("/:id", jwtMiddleware, cfg.DeviceHandler.DeleteDevice)
|
||||||
|
|
||||||
// Device management endpoints
|
// NEW: Token regeneration endpoint (JWT authentication required)
|
||||||
devices.GET("", jwtMiddleware, h.ListDevices)
|
devices.PUT("/:id/regenerate-token", jwtMiddleware, cfg.DeviceHandler.RegenerateDeviceToken)
|
||||||
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)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Verification**: Run `go build ./internal/router`
|
**Verification**: Run `go build ./internal/router`
|
||||||
@@ -573,13 +563,16 @@ deviceList[i] = DeviceInfo{
|
|||||||
|
|
||||||
**File**: `templates/devices.templ`
|
**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
|
```templ
|
||||||
for _, device := range devices {
|
for _, device := range devices {
|
||||||
@@ -647,12 +640,12 @@ for _, device := range devices {
|
|||||||
type="text"
|
type="text"
|
||||||
id="sync-url-{ device.ID }"
|
id="sync-url-{ device.ID }"
|
||||||
readonly
|
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"
|
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);"
|
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
|
||||||
/>
|
/>
|
||||||
<button
|
<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"
|
class="px-3 py-2 text-xs rounded hover:opacity-80"
|
||||||
style="background-color: var(--accent); color: white;"
|
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;"
|
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border); font-family: monospace;"
|
||||||
/>
|
/>
|
||||||
<button
|
<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"
|
class="px-3 py-2 text-xs rounded hover:opacity-80"
|
||||||
style="background-color: var(--accent); color: white;"
|
style="background-color: var(--accent); color: white;"
|
||||||
>
|
>
|
||||||
@@ -690,7 +683,7 @@ for _, device := range devices {
|
|||||||
|
|
||||||
<!-- Regenerate Token Button -->
|
<!-- Regenerate Token Button -->
|
||||||
<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"
|
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);"
|
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
|
- Regenerate button requires confirmation
|
||||||
- Clear setup instructions for each device type
|
- 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
|
#### 1.6.4 Add TypeScript Device Management
|
||||||
|
|
||||||
**File**: `web/src/device-management.ts` (CREATE NEW FILE)
|
**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
|
// 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' +
|
const confirmation = '⚠️ This will revoke current token and generate a new one.\n\n' +
|
||||||
'The old token will immediately stop working.\n\n' +
|
'The old token will immediately stop working.\n\n' +
|
||||||
'You will need to update your device configuration with new token.\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)
|
**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)
|
**Verification**: Test both authentication methods work (Kobo uses URL path, API clients can use Bearer)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 2: Kobo Integration
|
## Phase 2: Kobo Integration
|
||||||
|
|||||||
Reference in New Issue
Block a user