KI-Backend: Authorization-Header-Fix + TODO-Ergänzungen (4.5.11-4.5.14)

Login funktionierte, aber jede authentifizierte Folgeanfrage (Guthaben, KI-Anfrage)
meldete fälschlich eine abgelaufene Anmeldung — viele Apache/PHP-FPM-Hosting-Setups
reichen den Authorization-Header standardmäßig nicht an PHP durch. .htaccess erzwingt
jetzt die Weiterleitung, db.php liest zusätzlich REDIRECT_HTTP_AUTHORIZATION/
getallheaders() als Fallback. AiBackendUrl auf die echte deployte Domain gesetzt.

TODO.md: KI-Unterstützung auch für Stundenplanung (4.5.11), Frage zu einem
console.claude.ai-Agent für Standardkontext vs. app-internem Standard-Prompt
(4.5.12/4.5.13), sowie ein Planungsdiff für KI-Vorschläge auf Feldebene statt
grober Neu/Geändert-Markierung (4.5.14).
This commit is contained in:
2026-08-16 02:09:07 +02:00
parent 8546eabbdd
commit 7c7fe6dfcb
5 changed files with 71 additions and 4 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ public static class AppBootstrapper
/// editierbar, siehe Planungsdokument. Vor dem ersten produktiven Einsatz durch die tatsächlich /// editierbar, siehe Planungsdokument. Vor dem ersten produktiven Einsatz durch die tatsächlich
/// deployte Domain ersetzen. /// deployte Domain ersetzen.
/// </summary> /// </summary>
public const string AiBackendUrl = "https://REPLACE_ME.example.com/"; public const string AiBackendUrl = "https://backapi.science-teaching.de/";
/// <summary> /// <summary>
/// Vor <see cref="BuildServices"/> gesetzt, wenn die Datenbank passwortgeschützt ist /// Vor <see cref="BuildServices"/> gesetzt, wenn die Datenbank passwortgeschützt ist
+20 -2
View File
@@ -698,8 +698,26 @@ folgenden Punkte gehören direkt in `LehrerApp.Desktop`:
nicht-Desktop-Client (siehe `PlainEventStore`, `MapPlainSyncEndpoints`) — ein "Umplanung nicht-Desktop-Client (siehe `PlainEventStore`, `MapPlainSyncEndpoints`) — ein "Umplanung
nötig"-Flag käme darüber rein und würde nach dem Sync als Hinweis/Badge an der betroffenen nötig"-Flag käme darüber rein und würde nach dem Sync als Hinweis/Badge an der betroffenen
Stunde bzw. im Dashboard erscheinen, bis es am Desktop bearbeitet oder bewusst abgehakt wird. Stunde bzw. im Dashboard erscheinen, bis es am Desktop bearbeitet oder bewusst abgehakt wird.
- [ ] **4.5.11** KI-Unterstützung (4.5.9) auch für die Stundenplanung (Kapitel 4.3, `TimetableSlot`)
--- anbieten, nicht nur für Einheiten/Stunden im Verlaufsplan — z.B. Vorschläge beim Aufbau eines
neuen Stundenplans oder beim Ausgleich nach Änderungen. Gleiches Backend/gleiche Abrechnung
wie 4.5.9, aber eigenes Export/Import-Schema für die Stundenplan-Daten.
- [ ] **4.5.12** Offene Frage: Kann ein Agent/Project bei console.claude.ai eingerichtet werden,
um Standardinformationen (JSON-Schema, grundlegender Auftragskontext) dort dauerhaft zu
hinterlegen, statt sie bei jeder Anfrage über das eigene PHP-Backend mitschicken zu müssen?
Würde die Nutzer-Anweisung auf das eigentlich Fachliche reduzieren.
- [ ] **4.5.13** Alternative zu 4.5.12: statt eines extern gepflegten Agents ein app-interner
Standard-Prompt neben dem freien Nutzer-Prompt, der den grundlegenden Kontext (Schema,
Auftragsbeschreibung) automatisch mitliefert — der Nutzer muss ihn dann nicht jedes Mal
selbst formulieren. Ansatzweise bereits vorhanden (`ai-backend/plan.php`, `$systemPrompt`),
hier ginge es um eine bewusstere/erweiterte Ausgestaltung statt der aktuell schlanken Variante.
- [ ] **4.5.14** Planungsdiff: KI-Vorschläge (4.5.9) mit dem bereits Geplanten auf Feldebene
vergleichbar machen, nicht nur pauschal als "Neu"/"Geändert" markieren wie aktuell im
`AiAssistDialog`. Bei geänderten Stunden sollte sichtbar sein, was sich konkret unterscheidet
(Thema, einzelne Verlaufsplan-Phasen, Hausaufgabe, ...), und die Übernahme sollte sich sinnvoll
zusammenführen lassen — z.B. nur einzelne Phasen einer Stunde übernehmen statt zwingend die
ganze Stunde, oder eigene zwischenzeitliche Änderungen nicht versehentlich überschreiben,
falls sich die Einheit seit dem Absenden der Anfrage schon geändert hat.
## 5. Schülerdokumentation ## 5. Schülerdokumentation
+8
View File
@@ -17,3 +17,11 @@
RewriteEngine On RewriteEngine On
RewriteRule ^providers/ - [F,L] RewriteRule ^providers/ - [F,L]
RewriteRule ^scripts/ - [F,L] RewriteRule ^scripts/ - [F,L]
# Viele Apache/PHP-FPM-Setups reichen den Authorization-Header standardmäßig NICHT an PHP durch
# (login.php funktioniert dann, aber jede Anfrage mit Bearer-Token danach schlägt fehl, weil der
# Header bei PHP leer ankommt). Diese Zeile erzwingt die Weiterleitung — zusammen mit dem
# Mehrquellen-Fallback in db.php (ai_backend_authorization_header) deckt das die gängigen
# Hosting-Varianten ab.
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%1]
+16
View File
@@ -78,6 +78,22 @@ curl http://localhost:8000/status.php -H "Authorization: Bearer <token aus login
**Wichtig:** `AI_BACKEND_FAKE_PROVIDER` niemals auf dem produktiven Server setzen — sonst bekommt **Wichtig:** `AI_BACKEND_FAKE_PROVIDER` niemals auf dem produktiven Server setzen — sonst bekommt
die App nur die feste Test-Antwort statt echter KI-Vorschläge. die App nur die feste Test-Antwort statt echter KI-Vorschläge.
## Fehlerbehebung: "Anmeldung abgelaufen" direkt nach dem Anmelden
Login funktioniert, aber jede Anfrage danach (Guthaben, KI-Anfrage) meldet sofort eine
abgelaufene/ungültige Anmeldung? Typische Ursache: viele Apache/PHP-FPM-Hosting-Setups reichen
den `Authorization`-Header standardmäßig gar nicht erst an PHP durch — `login.php` braucht ihn
nicht, aber `status.php`/`plan.php` schon, und der kommt dort leer an.
Dagegen sind bereits zwei Absicherungen eingebaut:
- `.htaccess` erzwingt die Weiterleitung des Headers (`RewriteRule .* - [E=HTTP_AUTHORIZATION:%1]`).
- `db.php` liest zusätzlich `REDIRECT_HTTP_AUTHORIZATION` und `getallheaders()` als Fallback.
Falls es trotzdem weiter auftritt: `.htaccess`-Overrides sind auf manchem Hosting serverseitig
deaktiviert (`AllowOverride None`), oder es läuft nginx statt Apache (dort gilt `.htaccess`
grundsätzlich nicht) — dann hilft nur eine serverseitige Konfiguration durch den Hoster/Support
(z.B. bei nginx ein `fastcgi_param HTTP_AUTHORIZATION $http_authorization;`).
## Was hiermit NICHT geprüft ist ## Was hiermit NICHT geprüft ist
- Ob Anthropic zuverlässig valides JSON im erwarteten Schema liefert (reine Prompt-Qualitätsfrage, - Ob Anthropic zuverlässig valides JSON im erwarteten Schema liefert (reine Prompt-Qualitätsfrage,
+26 -1
View File
@@ -13,6 +13,31 @@ function ai_backend_db(array $config): PDO
]); ]);
} }
/**
* Liest den Authorization-Header — auf vielen Apache/PHP-FPM-Setups landet er NICHT in
* $_SERVER['HTTP_AUTHORIZATION'] (Header wird von PHP-FPM standardmäßig verworfen), sondern nur
* in $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] (nach der .htaccess-Weiterleitung, siehe dort) oder
* ist nur über getallheaders() erreichbar. Alle drei Quellen abklappern, statt sich auf eine zu
* verlassen — genau das war die Ursache für "Login klappt, danach sofort Session abgelaufen".
*/
function ai_backend_authorization_header(): string
{
if (!empty($_SERVER['HTTP_AUTHORIZATION'])) {
return $_SERVER['HTTP_AUTHORIZATION'];
}
if (!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
return $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
}
if (function_exists('getallheaders')) {
foreach (getallheaders() as $name => $value) {
if (strcasecmp($name, 'Authorization') === 0) {
return $value;
}
}
}
return '';
}
/** /**
* Bearer-Token aus dem Authorization-Header lesen, gegen tokens.token_hash prüfen * Bearer-Token aus dem Authorization-Header lesen, gegen tokens.token_hash prüfen
* (SHA-256, der Klartext wird nie gespeichert) und den zugehörigen aktiven User zurückgeben. * (SHA-256, der Klartext wird nie gespeichert) und den zugehörigen aktiven User zurückgeben.
@@ -20,7 +45,7 @@ function ai_backend_db(array $config): PDO
*/ */
function ai_backend_authenticate(PDO $pdo): array function ai_backend_authenticate(PDO $pdo): array
{ {
$header = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; $header = ai_backend_authorization_header();
if (!preg_match('/^Bearer\s+(.+)$/i', $header, $m)) { if (!preg_match('/^Bearer\s+(.+)$/i', $header, $m)) {
ai_backend_fail(401, 'Kein gültiges Token übermittelt.'); ai_backend_fail(401, 'Kein gültiges Token übermittelt.');
} }