feat: add Mattermost session auth for browser requests

- Add dual auth path in ServeHTTP: shared secret (daemon) OR Mattermost session (browser)
- Read-only endpoints (GET /sessions, GET /health) accept either auth method
- Write endpoints (POST, PUT, DELETE) still require shared secret
- Browser requests authenticated via Mattermost-User-Id header (auto-injected by MM server)
- Unauthenticated requests now properly rejected with 401

Fixes: Issue #5 Phase 1 - RHS Panel auth fix
This commit is contained in:
sol
2026-03-09 14:19:39 +00:00
parent 79d5e82803
commit 2d493d5c34

View File

@@ -16,19 +16,40 @@ import (
func (p *Plugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
// Auth middleware: validate shared secret for write operations.
// Read-only endpoints (GET /sessions, GET /health) are accessible to any
// authenticated Mattermost user — no shared secret required.
// Auth middleware: two auth paths.
// 1. Shared secret (Bearer token) — used by the daemon for write operations.
// 2. Mattermost session (Mattermost-User-Id header)used by browser requests.
// The Mattermost server automatically injects this header for authenticated
// requests routed through the plugin HTTP handler.
//
// Read-only endpoints (GET /sessions, GET /health) accept either auth method.
// Write endpoints (POST, PUT, DELETE) require the shared secret.
isReadOnly := r.Method == http.MethodGet && (path == "/api/v1/sessions" || path == "/api/v1/health")
if !isReadOnly {
config := p.getConfiguration()
hasSharedSecret := false
if config.SharedSecret != "" {
auth := r.Header.Get("Authorization")
expected := "Bearer " + config.SharedSecret
if auth != expected {
http.Error(w, `{"error": "unauthorized"}`, http.StatusUnauthorized)
hasSharedSecret = (auth == expected)
}
// Check Mattermost session auth (browser requests).
// The MM server injects Mattermost-User-Id for authenticated users.
mmUserID := r.Header.Get("Mattermost-User-Id")
hasMattermostSession := mmUserID != ""
if isReadOnly {
// Read-only: accept either shared secret OR Mattermost session
if !hasSharedSecret && !hasMattermostSession {
http.Error(w, `{"error": "unauthorized: valid Mattermost session or shared secret required"}`, http.StatusUnauthorized)
return
}
} else {
// Write operations: require shared secret (daemon auth)
if !hasSharedSecret {
http.Error(w, `{"error": "unauthorized"}`, http.StatusUnauthorized)
return
}
}