Easy_solution/docs/SSO_SLO_Documentation.md

186 lines
6.1 KiB
Markdown

# 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