# 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 └─> Paramètre redirect_app=easycheck propagé dans l'URL 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&redirect_app=easycheck 9. EasyCheck → Détecte from_portal=1 └─> Invalide la session (si elle existe encore) └─> Redirection vers portail login avec redirect_app 10. EasyPortal → Affichage page login └─> GET /login?redirect_app=easycheck 11. Utilisateur se reconnecte → LoginSubscriber détecte redirect_app=easycheck └─> 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' ``` ## Système de redirection multi-applications ### Paramètre `redirect_app` Pour gérer plusieurs applications SSO (EasyCheck, EasyAudit, EasyMaintenance, etc.), le système utilise un **paramètre URL `redirect_app`** au lieu de cookies. **Avantages** : - ✅ Fonctionne avec plusieurs onglets ouverts simultanément - ✅ Chaque onglet garde son contexte de déconnexion - ✅ Pas de conflit entre applications - ✅ Facilement extensible pour de nouvelles applications **Fonctionnement** : 1. Lors de la déconnexion depuis une application, elle envoie `redirect_app=nom_app` 2. Ce paramètre est propagé dans toutes les redirections de logout 3. Il arrive sur la page `/login?redirect_app=nom_app` 4. Après reconnexion, l'utilisateur est redirigé vers l'application d'origine **Configuration dans LoginSubscriber** : ```php $appUrls = [ 'easycheck' => $easycheckUrl . '/sso/login', 'easyaudit' => $easyauditUrl . '/sso/login', 'easymaintenance' => $easymaintenanceUrl . '/sso/login', ]; ``` ## Endpoints ### EasyCheck - API Logout **Route** : `POST /api/logout` **Description** : Endpoint API pour invalider la session EasyCheck sans redirection (non utilisé actuellement, prévu pour usage futur). **Réponse** : ```json { "success": true, "message": "Session invalidated successfully" } ``` ## 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 du paramètre `from_portal` 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** : Paramètre URL `redirect_app` pour rediriger l'utilisateur vers l'application d'origine après reconnexion 5. **Single Logout complet** : Toutes les sessions (portail + applications) sont toujours invalidées, quelle que soit l'origine de la déconnexion 6. **Multi-applications** : Support natif de plusieurs applications SSO sans conflit entre onglets