5d94572569
Vorbereitung für pod-unabhängige Sessions (k8s-Ziel). Ändert am Live-System NICHTS: AUTH_BACKEND='session' bleibt Default, die neuen Libs sind noch nirgends verdrahtet. - composer eingeführt: firebase/php-jwt ^6.11 + predis/predis ^2.4 (pure PHP, keine Redis-Extension nötig → läuft auf XAMPP wie im Prod-Image). vendor/ committed (kein Composer auf dem Server). - Jwt.php: HS256-Access-Token (issue/verify), decode() IMMER mit explizitem Algorithmus (mitigiert Algorithmus-Confusion). 8/8 Tests grün. - SessionStore.php: predis-gestützter Session/Refresh-Store (sess:<sid>, Sliding-TTL, revoke=DEL) — die widerrufbare Session hinter dem kurzen JWT. - app.php: vendor/autoload (guarded), Konstanten AUTH_BACKEND (Default session), REDIS_URL, JWT_TTL, JWT_REFRESH_TTL. JWT_SECRET bewusst ohne Default. - docker-compose.dev.yml: Redis (+ redis-commander UI) für lokale Dev-Parität. - .env.example um Auth-Block ergänzt. Nächste Phasen: $_SESSION-Zugriffe (293) durch Session:: kanalisieren → Backend-Umschalter in Session.php → doppelgleisiger Cutover → Reporting auf student_id-FK ziehen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
59 lines
1.9 KiB
PHP
59 lines
1.9 KiB
PHP
<?php
|
|
/**
|
|
* GeoGraSim — JWT Access-Token (HS256)
|
|
* ------------------------------------
|
|
* Dünner Wrapper über firebase/php-jwt. Stellt kurzlebige Access-Tokens aus
|
|
* und prüft sie. WICHTIG: decode() bekommt IMMER den expliziten Algorithmus
|
|
* (Key(secret, 'HS256')) — das mitigiert die Algorithmus-Confusion-Klasse
|
|
* (alg:none / RS↔HS-Verwechslung).
|
|
*
|
|
* Reine Bibliothek — wird erst im Cutover von Session.php genutzt. Solange
|
|
* AUTH_BACKEND='session' gilt, ruft nichts diese Klasse.
|
|
*/
|
|
|
|
use Firebase\JWT\JWT as FirebaseJWT;
|
|
use Firebase\JWT\Key;
|
|
|
|
class Jwt
|
|
{
|
|
const ALG = 'HS256';
|
|
|
|
private static function secret(): string
|
|
{
|
|
if (!defined('JWT_SECRET') || JWT_SECRET === '') {
|
|
throw new RuntimeException('JWT_SECRET fehlt — in .env setzen, sobald AUTH_BACKEND=jwt.');
|
|
}
|
|
return JWT_SECRET;
|
|
}
|
|
|
|
/**
|
|
* Access-Token ausstellen.
|
|
* @param array $claims z. B. ['sub'=>42, 'typ'=>'teacher', 'sid'=>'<redis-sid>', 'roles'=>[]]
|
|
*/
|
|
public static function issue(array $claims, ?int $ttl = null): string
|
|
{
|
|
$now = time();
|
|
$payload = array_merge([
|
|
'iss' => defined('BASE_URL') ? BASE_URL : 'geograsim',
|
|
'iat' => $now,
|
|
'nbf' => $now,
|
|
'exp' => $now + ($ttl ?? (defined('JWT_TTL') ? JWT_TTL : 900)),
|
|
], $claims);
|
|
return FirebaseJWT::encode($payload, self::secret(), self::ALG);
|
|
}
|
|
|
|
/**
|
|
* Access-Token prüfen. Gibt die Claims als Array zurück oder null
|
|
* (bei ungültiger Signatur, abgelaufen, noch nicht gültig, kaputt).
|
|
*/
|
|
public static function verify(string $token): ?array
|
|
{
|
|
try {
|
|
$decoded = FirebaseJWT::decode($token, new Key(self::secret(), self::ALG));
|
|
return (array) $decoded;
|
|
} catch (\Throwable $e) {
|
|
return null; // ExpiredException, SignatureInvalidException, …
|
|
}
|
|
}
|
|
}
|