#!/usr/bin/env python3 """ Script pour corriger la configuration du client Keycloak """ import requests import json import time class ClientFixer: def __init__(self, base_url: str = "http://localhost:8180"): self.base_url = base_url self.session = requests.Session() self.admin_token = None def get_admin_token(self) -> bool: """Obtient le token admin""" try: data = { "username": "admin", "password": "admin", "grant_type": "password", "client_id": "admin-cli" } response = self.session.post( f"{self.base_url}/realms/master/protocol/openid-connect/token", data=data, headers={"Content-Type": "application/x-www-form-urlencoded"} ) if response.status_code == 200: token_data = response.json() self.admin_token = token_data.get("access_token") return self.admin_token is not None except Exception as e: print(f"Erreur obtention token: {e}") return False def get_client_id(self, realm_name: str, client_id: str) -> str: """Obtient l'ID interne du client""" try: response = self.session.get( f"{self.base_url}/admin/realms/{realm_name}/clients", headers={"Authorization": f"Bearer {self.admin_token}"} ) if response.status_code == 200: clients = response.json() for client in clients: if client.get("clientId") == client_id: return client.get("id") except Exception as e: print(f"Erreur récupération client ID: {e}") return None def delete_client_if_exists(self, realm_name: str, client_id: str) -> bool: """Supprime le client s'il existe""" internal_id = self.get_client_id(realm_name, client_id) if internal_id: try: response = self.session.delete( f"{self.base_url}/admin/realms/{realm_name}/clients/{internal_id}", headers={"Authorization": f"Bearer {self.admin_token}"} ) if response.status_code == 204: print(f" ✓ Client {client_id} supprimé") return True except Exception as e: print(f" ⚠ Erreur suppression client: {e}") return False def create_client_complete(self, realm_name: str = "unionflow", client_id: str = "unionflow-mobile") -> bool: """Crée le client avec la configuration complète""" print(f"🔧 Création complète du client {client_id}...") # 1. Supprimer s'il existe self.delete_client_if_exists(realm_name, client_id) # 2. Créer le client avec une configuration complète client_data = { "clientId": client_id, "name": "UnionFlow Mobile App", "description": "Client pour l'application mobile UnionFlow", "enabled": True, "clientAuthenticatorType": "client-secret", "secret": "", "redirectUris": ["*"], "webOrigins": ["*"], "notBefore": 0, "bearerOnly": False, "consentRequired": False, "standardFlowEnabled": True, "implicitFlowEnabled": False, "directAccessGrantsEnabled": True, "serviceAccountsEnabled": False, "publicClient": True, "frontchannelLogout": False, "protocol": "openid-connect", "attributes": { "saml.assertion.signature": "false", "saml.force.post.binding": "false", "saml.multivalued.roles": "false", "saml.encrypt": "false", "saml.server.signature": "false", "saml.server.signature.keyinfo.ext": "false", "exclude.session.state.from.auth.response": "false", "saml_force_name_id_format": "false", "saml.client.signature": "false", "tls.client.certificate.bound.access.tokens": "false", "saml.authnstatement": "false", "display.on.consent.screen": "false", "saml.onetimeuse.condition": "false", "access.token.lifespan": "300", "client_credentials.use_refresh_token": "false" }, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": True, "nodeReRegistrationTimeout": -1, "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] } try: response = self.session.post( f"{self.base_url}/admin/realms/{realm_name}/clients", json=client_data, headers={ "Authorization": f"Bearer {self.admin_token}", "Content-Type": "application/json" } ) if response.status_code == 201: print(f" ✓ Client {client_id} créé avec succès") # Attendre un peu pour que la configuration soit prise en compte time.sleep(2) # Vérifier la configuration internal_id = self.get_client_id(realm_name, client_id) if internal_id: # Récupérer la configuration du client get_response = self.session.get( f"{self.base_url}/admin/realms/{realm_name}/clients/{internal_id}", headers={"Authorization": f"Bearer {self.admin_token}"} ) if get_response.status_code == 200: client_config = get_response.json() print(f" ✓ Configuration vérifiée:") print(f" - Public Client: {client_config.get('publicClient')}") print(f" - Direct Access Grants: {client_config.get('directAccessGrantsEnabled')}") print(f" - Standard Flow: {client_config.get('standardFlowEnabled')}") print(f" - Enabled: {client_config.get('enabled')}") return True else: print(f" ✗ Erreur création client: {response.status_code}") print(f" Réponse: {response.text}") return False except Exception as e: print(f" ✗ Exception création client: {e}") return False def test_client_auth(self, realm_name: str = "unionflow", client_id: str = "unionflow-mobile") -> bool: """Teste l'authentification avec le client""" print(f"🧪 Test d'authentification avec le client...") # Attendre un peu pour que tout soit synchronisé time.sleep(3) test_data = { "username": "marie.active", "password": "Marie123!", "grant_type": "password", "client_id": client_id } try: response = self.session.post( f"{self.base_url}/realms/{realm_name}/protocol/openid-connect/token", data=test_data, headers={"Content-Type": "application/x-www-form-urlencoded"} ) print(f" Status: {response.status_code}") if response.status_code == 200: token_data = response.json() if "access_token" in token_data: print(f" ✅ AUTHENTIFICATION RÉUSSIE !") print(f" ✓ Token reçu (longueur: {len(token_data['access_token'])})") return True else: print(f" ❌ Token manquant dans la réponse") else: print(f" ❌ Authentification échouée") print(f" Réponse: {response.text}") except Exception as e: print(f" ❌ Exception test auth: {e}") return False def fix_complete(self): """Correction complète du client""" print("=" * 80) print("🔧 CORRECTION DU CLIENT KEYCLOAK") print("=" * 80) print() if not self.get_admin_token(): print("❌ Impossible d'obtenir le token admin") return False print("✅ Token admin obtenu") print() # Créer le client if not self.create_client_complete(): print("❌ Échec de la création du client") return False print() # Tester l'authentification if self.test_client_auth(): print() print("=" * 80) print("🎉 CLIENT CORRIGÉ AVEC SUCCÈS !") print("=" * 80) print() print("🚀 Maintenant testez tous les comptes avec: python test_auth.py") return True else: print() print("=" * 80) print("⚠️ CLIENT CRÉÉ MAIS PROBLÈME D'AUTHENTIFICATION") print("=" * 80) return False def main(): fixer = ClientFixer() fixer.fix_complete() if __name__ == "__main__": main()