SSO + FOSUserBundle sous Symfony

image


Je vais vous montrer en quelques lignes comment mettre en place une connexion par SSO basique couplée à FOSUserBundle très simplement et rapidement. Le tout sous Symfony.

A vous de performer la sécurisation de votre architecture 😉


Le principe d'un Single Sign-On repose sur le principe de connexion à une application via une URL.
Dans l'idée, c'est de pouvoir se connecter grâce à un lien unique.

 

Lors de la mise en place, il faut se poser les bonnes questions :

Qui dit URL, dit route...
Qui dit route, dit :
- Questionnement sur l'accès...
- La logique contrôleur...

 

Nous allons commencer par bien configurer et récupérer le nom du Pare-feu (firewalls) principal utilisé qui s'appelle  main :

# app/config/security.yml

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt

    role_hierarchy:
        # [...]

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email

    firewalls:
        # [...]
        main:
            # [...]

    access_control:
        - { path: ^/sso/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        # [...]

 

 

Pour finir,  il faudra créer notre contrôleur qui sera dédié au SSO en 4 étapes :

  1. Dans un premier temps vérifier que l'utilisateur existe en base
  2. Établir la connexion
  3. Informer aux événements (events) qu'un utilisateur s'est connecté
  4. Faire une redirection vers une page souhaitée via la route
<?php

namespace App\Controller;

use App\Entity\User;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

/**
 * @Route("/sso")
 */
class SSOController extends Controller
{
    /**
     * Processus basique d'une connexion par SSO.
     *
     * @Route("/{username}", name="sso_connect", methods="GET")
     *
     * @param Request         $request
     * @param LoggerInterface $logger
     * @param                 $username
     *
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function connect(Request $request, LoggerInterface $logger, $username): \Symfony\Component\HttpFoundation\RedirectResponse
    {
        // Initialisation de l'Entity Manager
        $em = $this->getDoctrine()->getManager();

        // Récupération de l'utilisateur
        $user = $em->getRepository(User::class)->findOneBy(['username' => $username]);

        if ($user) {
            // On va connecter l'utilisateur !
            $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
            $this->get('security.token_storage')->setToken($token);

            // On informe qu'une connexion vien d'avoir lieu aux événements (events).
            $event = new InteractiveLoginEvent($request, $token);

            $this->get('event_dispatcher')->dispatch('security.interactive_login', $event);
            $logger->info('Connexion de l\'utilisateur par SSO : '.$user->getUsername());

            // Une redirection vers la route "homepage"
            return $this->redirectToRoute('homepage');
        }

        throw $this->createNotFoundException();
    }
}

 

Et voila, terminado !

 

La connexion par SSO est à vous ! La classe non ? 😎