fix: Terminal Auth per JWT-Token statt leerem API-Key

- TerminalModal nutzt jetzt JWT aus localStorage (api.getToken()) fuer WS-Auth
- Backend Middleware: ?token= Query-Parameter fuer WebSocket JWT-Auth
- Behebt: api_key= leer -> 401 -> Terminal nicht erreichbar
This commit is contained in:
cynfo3000 2026-03-09 23:39:38 +01:00
parent 358682e5f3
commit e9a47bee26
2 changed files with 19 additions and 5 deletions

View File

@ -73,7 +73,7 @@ func (c *APIKeyCache) IsValid(key string) bool {
// CombinedAuthWithCache - API-Key (aus DB-Cache) ODER JWT Token // CombinedAuthWithCache - API-Key (aus DB-Cache) ODER JWT Token
func CombinedAuthWithCache(cache *APIKeyCache, auth *AuthHandler, next http.Handler) http.Handler { func CombinedAuthWithCache(cache *APIKeyCache, auth *AuthHandler, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Try API key first // Try API key first (Header oder Query-Parameter)
key := r.Header.Get("X-API-Key") key := r.Header.Get("X-API-Key")
if key == "" { if key == "" {
key = r.URL.Query().Get("api_key") key = r.URL.Query().Get("api_key")
@ -83,11 +83,16 @@ func CombinedAuthWithCache(cache *APIKeyCache, auth *AuthHandler, next http.Hand
return return
} }
// Try JWT // Try JWT — Authorization Header oder ?token= Query-Parameter (fuer WebSocket)
var jwtToken string
authHeader := r.Header.Get("Authorization") authHeader := r.Header.Get("Authorization")
if strings.HasPrefix(authHeader, "Bearer ") { if strings.HasPrefix(authHeader, "Bearer ") {
token := strings.TrimPrefix(authHeader, "Bearer ") jwtToken = strings.TrimPrefix(authHeader, "Bearer ")
claims, err := auth.ValidateToken(token) } else if t := r.URL.Query().Get("token"); t != "" {
jwtToken = t
}
if jwtToken != "" {
claims, err := auth.ValidateToken(jwtToken)
if err == nil { if err == nil {
ctx := context.WithValue(r.Context(), UserContextKey, claims) ctx := context.WithValue(r.Context(), UserContextKey, claims)
next.ServeHTTP(w, r.WithContext(ctx)) next.ServeHTTP(w, r.WithContext(ctx))

View File

@ -4,6 +4,7 @@ import { FitAddon } from '@xterm/addon-fit'
import '@xterm/xterm/css/xterm.css' import '@xterm/xterm/css/xterm.css'
import { useSettingsStore } from '../stores/settings' import { useSettingsStore } from '../stores/settings'
import { API_KEY } from '../config' import { API_KEY } from '../config'
import api from '../api/client'
export default function TerminalModal({ agentId, agentName, onClose }) { export default function TerminalModal({ agentId, agentName, onClose }) {
const termRef = useRef(null) const termRef = useRef(null)
@ -13,6 +14,8 @@ export default function TerminalModal({ agentId, agentName, onClose }) {
const [status, setStatus] = useState('connecting') // connecting | open | error | closed const [status, setStatus] = useState('connecting') // connecting | open | error | closed
const backendHost = useSettingsStore.getState().getBackendHost() const backendHost = useSettingsStore.getState().getBackendHost()
// JWT-Token bevorzugen (Browser ist per Login authentifiziert), API-Key als Fallback
const jwtToken = api.getToken()
const apiKey = API_KEY const apiKey = API_KEY
useEffect(() => { useEffect(() => {
@ -58,7 +61,13 @@ export default function TerminalModal({ agentId, agentName, onClose }) {
const cols = term.cols || 220 const cols = term.cols || 220
const rows = term.rows || 50 const rows = term.rows || 50
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:' const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
const wsUrl = `${wsProtocol}//${window.location.host}/api/v1/agents/${agentId}/terminal?cols=${cols}&rows=${rows}&api_key=${apiKey}` // JWT bevorzugen, API-Key als Fallback (API-Key nur wenn konfiguriert)
const authParam = jwtToken
? `token=${encodeURIComponent(jwtToken)}`
: apiKey
? `api_key=${encodeURIComponent(apiKey)}`
: ''
const wsUrl = `${wsProtocol}//${window.location.host}/api/v1/agents/${agentId}/terminal?cols=${cols}&rows=${rows}&${authParam}`
const ws = new WebSocket(wsUrl) const ws = new WebSocket(wsUrl)
ws.binaryType = 'arraybuffer' ws.binaryType = 'arraybuffer'