feat: support multiple device authentication methods
- Bearer token in Authorization header (KOReader, API clients) - URL path parameter (Kobo sync: /api/sync/kobo/:token/...) - Query parameter (OPDS: ?token=...) - Update Kobo sync routes to use token in path - Add authentication method documentation to OPDS routes
This commit is contained in:
@@ -36,28 +36,57 @@ func NewDeviceAuthMiddleware(db *database.Queries) *DeviceAuthMiddleware {
|
||||
|
||||
func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
var device database.Devices
|
||||
var err error
|
||||
var token string
|
||||
var urlToken string
|
||||
var queryToken string
|
||||
|
||||
// Method 1: Try Bearer token header (KOReader, API clients, OPDS)
|
||||
authHeader := c.Request().Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
return c.JSON(http.StatusUnauthorized, map[string]string{
|
||||
"error": "missing authorization header",
|
||||
})
|
||||
if authHeader != "" {
|
||||
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||
return c.JSON(http.StatusUnauthorized, map[string]string{
|
||||
"error": "invalid authorization header format",
|
||||
})
|
||||
}
|
||||
|
||||
token = strings.TrimPrefix(authHeader, "Bearer ")
|
||||
device, err = m.db.GetDeviceByAuthToken(c.Request().Context(), token)
|
||||
if err == nil {
|
||||
// Found device via Bearer token, continue to validation
|
||||
goto validateDevice
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||
return c.JSON(http.StatusUnauthorized, map[string]string{
|
||||
"error": "invalid authorization header format",
|
||||
})
|
||||
// Method 2: Try URL path parameter (Kobo sync, OPDS)
|
||||
// Route format: /api/sync/kobo/:token/...
|
||||
urlToken = c.Param("token")
|
||||
if urlToken != "" {
|
||||
device, err = m.db.GetDeviceByAuthToken(c.Request().Context(), urlToken)
|
||||
if err == nil {
|
||||
// Found device via URL path token, continue to validation
|
||||
goto validateDevice
|
||||
}
|
||||
}
|
||||
|
||||
token := strings.TrimPrefix(authHeader, "Bearer ")
|
||||
|
||||
device, err := m.db.GetDeviceByAuthToken(c.Request().Context(), token)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusUnauthorized, map[string]string{
|
||||
"error": "invalid device token",
|
||||
})
|
||||
// Method 3: Try query parameter (OPDS catalog access)
|
||||
// URL format: /opds/devices/:deviceId/catalog?token=...
|
||||
queryToken = c.QueryParam("token")
|
||||
if queryToken != "" {
|
||||
device, err = m.db.GetDeviceByAuthToken(c.Request().Context(), queryToken)
|
||||
if err == nil {
|
||||
// Found device via query parameter token, continue to validation
|
||||
goto validateDevice
|
||||
}
|
||||
}
|
||||
|
||||
// All authentication methods failed
|
||||
return c.JSON(http.StatusUnauthorized, map[string]string{
|
||||
"error": "authentication required - use Bearer token or API key",
|
||||
})
|
||||
|
||||
validateDevice:
|
||||
if !device.SyncEnabled.Bool || !device.SyncEnabled.Valid {
|
||||
return c.JSON(http.StatusForbidden, map[string]string{
|
||||
"error": "device sync is disabled",
|
||||
|
||||
Reference in New Issue
Block a user