Paillasse/portail/app/Services/Visio/JetonVisio.php
Adam Belkacemi ec4eae3744 ajoute la visio de classe : Jitsi auto-hébergé, jetons du portail, prof seul modérateur
Jitsi (docker-jitsi-meet, version figée) derrière Nginx sur meet.syllabuslab.fr,
accès par jeton JWT signé par le portail, modération du micro, mains levées
synchronisées avec l'application. Pare-feu : UDP 10000 et pont interne
br-visio (accords d'Adam). Jicofo n'authentifie plus lui-même : il donnait
le rôle de propriétaire aux élèves. Capacité ramenée à 20 VM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:35:35 +02:00

52 lines
1.7 KiB
PHP

<?php
namespace App\Services\Visio;
use App\Models\Seance;
use RuntimeException;
/**
* Jeton JWT (HS256) qui ouvre une salle Jitsi : Prosody le vérifie avec le
* même secret. Le nom vient du jeton et ne se modifie pas ; seul le prof a
* moderator = true (module natif token_affiliation de Prosody).
*/
class JetonVisio
{
public function pour(Seance $seance, string $identifiant, string $nom, bool $moderateur): string
{
$secret = config('visio.jwt.secret');
if (! is_string($secret) || strlen($secret) < 32 || $seance->visio_salle === null) {
throw new RuntimeException('Visio non configurée ou salle non ouverte.');
}
$maintenant = time();
$charge = [
'iss' => config('visio.jwt.app_id'),
'aud' => config('visio.jwt.audience'),
'sub' => '*',
'room' => $seance->visio_salle,
'iat' => $maintenant,
'nbf' => $maintenant - 10,
'exp' => $maintenant + 60 * (int) config('visio.jwt.duree_minutes'),
'context' => [
'user' => [
'id' => $identifiant,
'name' => $nom,
'moderator' => $moderateur,
],
],
];
$entete = $this->base64Url(json_encode(['alg' => 'HS256', 'typ' => 'JWT']));
$corps = $this->base64Url(json_encode($charge, JSON_UNESCAPED_UNICODE));
$signature = $this->base64Url(hash_hmac('sha256', "{$entete}.{$corps}", $secret, true));
return "{$entete}.{$corps}.{$signature}";
}
private function base64Url(string $donnees): string
{
return rtrim(strtr(base64_encode($donnees), '+/', '-_'), '=');
}
}