feat(plugins): Implement plugin API key management and authentication middleware

The purpose of this is to allow plugins to access certain internal APIs via

- Added PluginAPIKey and APIKeyManager for managing API keys associated with plugins.
- Introduced PluginAuthMiddleware to handle API key validation for plugin requests.
- Updated RouterDef to support plugin accessible endpoints with authentication.
- Modified various API registration functions to include plugin accessibility checks.
- Enhanced plugin lifecycle management to generate and revoke API keys as needed.
- Updated plugin specifications to include permitted API endpoints for access control.
This commit is contained in:
Anthony Rubick
2025-07-17 19:50:57 -07:00
parent 70b1ccfa6e
commit dd93f9a2c4
10 changed files with 470 additions and 191 deletions

View File

@@ -0,0 +1,77 @@
// Handles the API-Key based authentication for plugins
package auth
import (
"net/http"
"strings"
)
// PluginAuthMiddleware provides authentication middleware for plugin API requests
type PluginAuthMiddleware struct {
apiKeyManager *APIKeyManager
}
// NewPluginAuthMiddleware creates a new plugin authentication middleware
func NewPluginAuthMiddleware(apiKeyManager *APIKeyManager) *PluginAuthMiddleware {
return &PluginAuthMiddleware{
apiKeyManager: apiKeyManager,
}
}
// ValidatePluginAPIRequest validates an API request with plugin API key for a specific endpoint
func (m *PluginAuthMiddleware) ValidatePluginAPIRequest(endpoint string, method string, apiKey string) (*PluginAPIKey, error) {
return m.apiKeyManager.ValidateAPIKeyForEndpoint(endpoint, method, apiKey)
}
// WrapHandler wraps an HTTP handler with plugin authentication middleware
func (m *PluginAuthMiddleware) WrapHandler(endpoint string, handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// First, remove any existing plugin authentication headers
r.Header.Del("X-Zoraxy-Plugin-ID")
r.Header.Del("X-Zoraxy-Plugin-Auth")
// Check for API key in the Authorization header
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
// No authorization header, proceed with normal authentication
handler(w, r)
return
}
// Check if it's a plugin API key (Bearer token)
if !strings.HasPrefix(authHeader, "Bearer ") {
// Not a Bearer token, proceed with normal authentication
handler(w, r)
return
}
// Extract the API key
apiKey := strings.TrimPrefix(authHeader, "Bearer ")
// Validate the API key for this endpoint
pluginAPIKey, err := m.ValidatePluginAPIRequest(endpoint, r.Method, apiKey)
if err != nil {
// Invalid API key or endpoint not permitted
http.Error(w, "Unauthorized: Invalid API key or endpoint not permitted", http.StatusUnauthorized)
return
}
// Add plugin information to the request context
r.Header.Set("X-Zoraxy-Plugin-ID", pluginAPIKey.PluginID)
r.Header.Set("X-Zoraxy-Plugin-Auth", "true")
// Call the original handler
handler(w, r)
}
}
// GetPluginIDFromRequest extracts the plugin ID from the request if authenticated via plugin API key
func GetPluginIDFromRequest(r *http.Request) string {
return r.Header.Get("X-Plugin-ID")
}
// IsPluginAuthenticated checks if the request is authenticated via plugin API key
func IsPluginAuthenticated(r *http.Request) bool {
return r.Header.Get("X-Plugin-Auth") == "true"
}