# Documentation SSO/SLO - EasyPortal & EasyCheck ## Vue d'ensemble Cette documentation décrit l'implémentation du **Single Sign-On (SSO)** et du **Single Logout (SLO)** entre deux applications Symfony : - **EasyPortal** : Serveur d'autorisation OAuth2 (Identity Provider) - **EasyCheck** : Application cliente OAuth2 ## Architecture ``` ┌─────────────────┐ ┌─────────────────┐ │ EasyPortal │ │ EasyCheck │ │ (OAuth2 Server)│◄──────────────────►│ (OAuth2 Client) │ │ │ │ │ │ - Authentifie │ │ - Utilise le │ │ - Émet tokens │ │ token OAuth2 │ │ - Révoque │ │ - Valide token │ └─────────────────┘ └─────────────────┘ ``` ## Single Sign-On (SSO) ### Principe L'utilisateur s'authentifie **une seule fois** sur le portail et accède ensuite à toutes les applications sans re-saisir ses identifiants. ### Flux d'authentification ``` 1. Utilisateur → EasyCheck └─> Pas de session active 2. EasyCheck → Redirection vers EasyPortal └─> /authorize?client_id=...&redirect_uri=... 3. Utilisateur → Connexion sur EasyPortal └─> Login/Password ou session existante 4. EasyPortal → Redirection vers EasyCheck └─> /sso_check?code=AUTHORIZATION_CODE 5. EasyCheck → Échange du code contre un token └─> POST /token avec authorization_code └─> Reçoit access_token + refresh_token 6. EasyCheck → Création de session locale └─> Stockage du token en session └─> Utilisateur connecté ``` ## Single Logout (SLO) ### Principe Lorsqu'un utilisateur se déconnecte d'une application, il est **automatiquement déconnecté de toutes les applications** SSO via un appel API asynchrone, évitant les boucles de redirections. ### Flux de déconnexion depuis EasyCheck ``` 1. Utilisateur → Clic "Déconnexion" sur EasyCheck └─> GET /logout 2. Symfony → Invalide la session EasyCheck └─> Session détruite, cookies supprimés 3. LogoutSubscriber → Interception de l'événement └─> Détecte que ce n'est pas une déconnexion depuis le portail 4. EasyCheck → Redirection vers portail └─> GET https://portail.../sso_logout?from_easycheck=1 5. EasyPortal → Révocation des tokens OAuth2 └─> Tous les access_token de l'utilisateur sont révoqués └─> Cookie logout_origin=easycheck créé (5 min) 6. EasyPortal → Redirection vers /logout └─> GET /logout 7. Symfony → Invalide la session EasyPortal └─> Session détruite 8. LogoutSubscriber → Redirection vers EasyCheck └─> GET https://check.../logout?from_portal=1 9. EasyCheck → Détecte from_portal=1 └─> Invalide la session (si elle existe encore) └─> Redirection vers portail login 10. EasyPortal → Affichage page login └─> GET /login 11. Utilisateur se reconnecte → LoginSubscriber détecte cookie logout_origin └─> Redirection automatique vers EasyCheck /sso/login ``` ### Flux de déconnexion depuis EasyPortal ``` 1. Utilisateur → Clic "Déconnexion" sur EasyPortal └─> GET /sso_logout 2. EasyPortal → Révocation des tokens OAuth2 └─> Tous les access_token de l'utilisateur sont révoqués 3. EasyPortal → Redirection vers /logout └─> GET /logout 4. Symfony → Invalide la session EasyPortal └─> Session détruite 5. LogoutSubscriber → Redirection vers EasyCheck └─> GET https://check.../logout?from_portal=1 6. EasyCheck → Détecte from_portal=1 └─> Invalide la session EasyCheck └─> Session détruite, cookies supprimés 7. EasyCheck → Redirection finale vers portail └─> GET https://portail.../login ``` ## Variables d'environnement ### EasyCheck (.env) ```bash # URL du serveur SSO (EasyPortal) SSO_URL='https://portail.solutions-easy.moi' # Configuration OAuth2 OAUTH_CLIENT_ID='easycheck-client-id' OAUTH_CLIENT_SECRET='secret-key' ``` ### EasyPortal (.env) ```bash # URL de l'application cliente (EasyCheck) EASYCHECK_URL='https://check.solutions-easy.moi' # Configuration OAuth2 Server OAUTH_PRIVATE_KEY=%kernel.project_dir%/config/jwt/private.key OAUTH_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.key OAUTH_PASSPHRASE='passphrase' OAUTH_ENCRYPTION_KEY='encryption-key' ``` ## Endpoints ### EasyCheck - API Logout **Route** : `POST /api/logout` **Description** : Endpoint API pour invalider la session EasyCheck sans redirection. Utilisé par le portail lors du SLO. **Réponse** : ```json { "success": true, "message": "Session invalidated successfully" } ``` **Comportement** : - Invalide la session utilisateur - Ne redirige pas (contrairement à `/logout`) - Timeout de 2 secondes côté portail - Si l'appel échoue, le logout du portail continue quand même ## Points importants ### Sécurité 1. **CSRF désactivé sur logout** : Les routes de logout utilisent `enable_csrf: false` car ce sont des liens GET simples 2. **Tokens révoqués** : Lors du logout, tous les access_token de l'utilisateur sont révoqués côté portail 3. **Sessions invalidées** : Les sessions PHP sont complètement détruites des deux côtés 4. **Cookies supprimés** : Les cookies de session sont explicitement supprimés ### Architecture 1. **Pas de boucles infinies** : Utilisation de paramètres `from_portal` et `from_easycheck` pour éviter les boucles de redirections 2. **Déconnexion bidirectionnelle** : Chaque application invalide la session de l'autre lors de la déconnexion 3. **Flux prévisible** : Chaque déconnexion suit un chemin clair avec des paramètres explicites 4. **Retour automatique** : Cookie `logout_origin` pour rediriger l'utilisateur vers l'application d'origine après reconnexion 5. **Single Logout complet** : Les deux sessions (portail + EasyCheck) sont toujours invalidées, quelle que soit l'origine de la déconnexion