revision gestion cookie

This commit is contained in:
2025-09-22 15:17:33 +02:00
parent bcf31c3fbb
commit 0aac7b2c51
7 changed files with 46 additions and 38 deletions

View File

@@ -1,15 +1,11 @@
:80 { :80 {
# Force HTTP, désactive auto-HTTPS # Force HTTP pour le développement
http://localhost { http://localhost {
root * /app/public
php_server php_server
route { encode gzip
file_server
@php path *.php /index.php
php_fastcgi @php unix//run/php/php-fpm.sock
}
log { log {
output stdout output stdout
} }
encode gzip
} }
} }

View File

@@ -4,6 +4,8 @@ RUN apt-get update && apt-get install -y \
unzip \ unzip \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN cp $PHP_INI_DIR/php.ini-development ./php.ini
# Installe Composer # Installe Composer
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
# Installe les extensions PHP nécessaires pour Symfony (pdo_mysql par exemple, si tu utilises MySQL) # Installe les extensions PHP nécessaires pour Symfony (pdo_mysql par exemple, si tu utilises MySQL)

View File

@@ -4,11 +4,14 @@ namespace App\Controller;
use App\Form\CodeType; use App\Form\CodeType;
use App\Hydra\Client; use App\Hydra\Client;
use App\Hydra\HydraService;
use App\Service\CodeService; use App\Service\CodeService;
use App\Service\CookieService; use App\Service\CookieService;
use App\Service\DeviceService; use App\Service\DeviceService;
use App\Service\MailerService; use App\Service\MailerService;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpClient\Exception\ClientException;
use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Exception\BadRequestException; use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -17,6 +20,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email; use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class MainController extends AbstractController class MainController extends AbstractController
{ {
@@ -24,7 +28,9 @@ class MainController extends AbstractController
private readonly CodeService $codeService, private readonly CodeService $codeService,
private readonly Client $client, private readonly Client $client,
private readonly CookieService $cookieService, private readonly CookieService $cookieService,
private readonly MailerService $mailer private readonly MailerService $mailer,
private readonly UrlGeneratorInterface $router,
private readonly HydraService $hydraService
){ ){
@@ -34,13 +40,12 @@ class MainController extends AbstractController
{ {
return new Response('<h1>Hello world</h1>'); return new Response('<h1>Hello world</h1>');
} }
#[Route('/2fa', name: 'app_2fa')] #[Route('/2fa', name: 'app_2fa', methods: ['GET', 'POST'])]
public function doubleFacteur(Request $request): Response public function doubleFacteur(Request $request, LoggerInterface $logger): Response
{ {
$loginChallenge = $request->query->get('loginchallenge'); $loginChallenge = $request->query->get('loginchallenge');
$identifier = $request->query->get('identifier'); $identifier = $request->query->get('identifier');
$res = $this->client->fetchLoginRequestInfo($loginChallenge); $res = $this->client->fetchLoginRequestInfo($loginChallenge);
$loginRequestInfo = $res->toArray();
if (200 !== $res->getStatusCode()) { if (200 !== $res->getStatusCode()) {
throw new BadRequestException(); throw new BadRequestException();
} }
@@ -50,6 +55,7 @@ class MainController extends AbstractController
'subject' => $identifier, 'subject' => $identifier,
'remember' => true, 'remember' => true,
])->toArray(); ])->toArray();
// dd($loginAcceptRes);
return new RedirectResponse($loginAcceptRes['redirect_to']); return new RedirectResponse($loginAcceptRes['redirect_to']);
} }
@@ -62,22 +68,28 @@ class MainController extends AbstractController
if ($form->isSubmitted() && $form->isValid()){ if ($form->isSubmitted() && $form->isValid()){
$cookie = null; $cookie = null;
if($form->get('remember_device')){ if($form->get('remember_device')->getData()){
$cookie = $this->cookieService->createCookie($identifier); $cookie = $this->cookieService->createCookie($identifier);
} }
try{
$loginAcceptRes = $this->client->acceptLoginRequest($loginChallenge, [ $loginAcceptRes = $this->client->acceptLoginRequest($loginChallenge, [
'subject' => $identifier, 'subject' => $identifier,
'remember' => true, 'remember' => true,
])->toArray(); ])->toArray();
}catch(ClientException $e){
dump($e);
}
dump($loginAcceptRes);
$response = new RedirectResponse($loginAcceptRes['redirect_to']); $response = new RedirectResponse($loginAcceptRes['redirect_to']);
if ($cookie !== null) {
$response->headers->setCookie($cookie);
}
null !== $cookie ?: $response->headers->setCookie($cookie);
return $response; return $response;
} }
dump('here');
return $this->render('base.html.twig', [ return $this->render('base.html.twig', [
'form'=>$form 'form'=>$form
]); ]);

View File

@@ -26,7 +26,7 @@ class CodeType extends AbstractType
], ],
]) ])
->add('remember_device', CheckboxType::class, [ ->add('remember_device', CheckboxType::class, [
'label'=>('Se souvenir de cet ordinateur'), 'label'=>'Se souvenir de cet ordinateur',
'required'=> false, 'required'=> false,
'mapped'=>false, 'mapped'=>false,
]) ])

View File

@@ -52,10 +52,7 @@ class HydraService extends AbstractController
} }
$consentRequestInfo = $this->client->fetchConsentRequestInfo($challenge)->toArray(); $consentRequestInfo = $this->client->fetchConsentRequestInfo($challenge)->toArray();
$user = $this->getUser();
if (!$user instanceof User) {
throw new AccessDeniedException('Utilisateur non autorisé.');
}
$consentAcceptResponse = $this->client->acceptConsentRequest($consentRequestInfo['challenge'], [ $consentAcceptResponse = $this->client->acceptConsentRequest($consentRequestInfo['challenge'], [
'grant_scope' => $consentRequestInfo['requested_scope'], 'grant_scope' => $consentRequestInfo['requested_scope'],
'session' => [ 'session' => [
@@ -63,6 +60,7 @@ class HydraService extends AbstractController
], ],
])->toArray(); ])->toArray();
dd(vars: $consentAcceptResponse);
return new RedirectResponse($consentAcceptResponse['redirect_to']); return new RedirectResponse($consentAcceptResponse['redirect_to']);
} }

View File

@@ -11,30 +11,33 @@ class CookieService
public function __construct( public function __construct(
private readonly ParameterBagInterface $params private readonly ParameterBagInterface $params
){} ){}
private const COOKIE_2FA = 'user_info-2fa'; private const COOKIE_2FA = 'remember_2fa';
public function isValid(Request $request, string $login): bool public function isValid(Request $request, string $login): bool
{ {
$cookieValue = $request->cookies->get(self::COOKIE_2FA); $cookieValue = $request->cookies->get(self::COOKIE_2FA);
if(!$cookieValue){ if(!$cookieValue){
return false; return false;
} }
[$encodedData, $signature] = explode('.', $cookieValue); [$encodedData, $signature] = explode('.', $cookieValue);
$dataJson = base64_decode($encodedData); $dataJson = base64_decode($encodedData);
$secret = $this->params->get('kernel.secret'); $secret = $this->params->get('kernel.secret');
if (hash_hmac('sha256', $dataJson, $secret) !== $signature) { if (hash_hmac('sha256', $dataJson, $secret) !== $signature) {
return false; // Signature invalide return false;
} }
$data = json_decode($dataJson, true); $data = json_decode($dataJson, true);
if (!$data || $data['login'] !== $login) { if (!$data || $data['login'] !== $login) {
return false; // Login non correspondant return false;
} }
// Recalculer la validité avec la durée paramétrable actuelle $duration = new \DateInterval($this->params->get('app.2fa_remember_duration'));
$duration = new \DateInterval($this->params->get('app.2fa_remember_duration')); // ex. P30D $createdAt = (new \DateTimeImmutable())->setTimestamp($data['created_at']);
$expirationTime = $data['created_at'] + $duration->format('%s'); // Convertir en secondes $expirationTime = $createdAt->add($duration)->getTimestamp();
if (time() > $expirationTime) { if (time() > $expirationTime) {
return false; // Expiré selon la durée actuelle return false;
} }
return true; return true;
@@ -63,7 +66,7 @@ class CookieService
->withSecure($this->params->get('kernel.environment') === 'prod') ->withSecure($this->params->get('kernel.environment') === 'prod')
->withHttpOnly(true) ->withHttpOnly(true)
->withSameSite('Lax') ->withSameSite('Lax')
->withPath('/hydra-otp') ->withPath('/')
; ;
} }
} }

View File

@@ -7,9 +7,6 @@
{% block stylesheets %} {% block stylesheets %}
{% endblock %} {% endblock %}
{% block javascripts %}
{% block importmap %}{{ importmap('app') }}{% endblock %}
{% endblock %}
<style> <style>
body { body {
display: flex; display: flex;
@@ -30,8 +27,8 @@
<div syle=""> <div syle="">
{{ form_start(form) }} {{ form_start(form) }}
{{ form_errors(form.code) }}
{{ form_row(form.code) }} {{ form_row(form.code) }}
{{ form_row(form.remember_device) }}
{{form_end(form)}} {{form_end(form)}}
</div> </div>
{% endblock %} {% endblock %}