#!/usr/bin/env python3 """ Script pour corriger la configuration du client unionflow-mobile Spécifiquement les redirect_uri pour l'application mobile """ import requests import json class ClientRedirectFixer: 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_internal_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 fix_client_configuration(self, realm_name: str = "unionflow", client_id: str = "unionflow-mobile") -> bool: """Corrige la configuration du client pour mobile""" print(f"🔧 Correction de la configuration du client {client_id}...") # 1. Obtenir l'ID interne du client internal_id = self.get_client_internal_id(realm_name, client_id) if not internal_id: print(f" ❌ Client {client_id} non trouvé") return False print(f" ✓ Client trouvé (ID interne: {internal_id})") # 2. Configuration correcte pour une application mobile client_config = { "id": internal_id, "clientId": client_id, "name": "UnionFlow Mobile App", "description": "Client pour l'application mobile UnionFlow", "enabled": True, "clientAuthenticatorType": "client-secret", "secret": "", "redirectUris": [ "http://localhost:*", "http://127.0.0.1:*", "http://192.168.1.11:*", "unionflow://oauth/callback", "unionflow://login", "com.unionflow.mobile://oauth", "urn:ietf:wg:oauth:2.0:oob" ], "webOrigins": [ "http://localhost", "http://127.0.0.1", "http://192.168.1.11", "+" ], "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", "oauth2.device.authorization.grant.enabled": "false", "oidc.ciba.grant.enabled": "false" }, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": True, "nodeReRegistrationTimeout": -1, "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] } try: # 3. Mettre à jour la configuration response = self.session.put( f"{self.base_url}/admin/realms/{realm_name}/clients/{internal_id}", json=client_config, headers={ "Authorization": f"Bearer {self.admin_token}", "Content-Type": "application/json" } ) if response.status_code == 204: print(f" ✅ Configuration du client mise à jour") # 4. Vérifier la configuration verify_response = self.session.get( f"{self.base_url}/admin/realms/{realm_name}/clients/{internal_id}", headers={"Authorization": f"Bearer {self.admin_token}"} ) if verify_response.status_code == 200: config = verify_response.json() print(f" ✓ Configuration vérifiée:") print(f" - Public Client: {config.get('publicClient')}") print(f" - Direct Access Grants: {config.get('directAccessGrantsEnabled')}") print(f" - Standard Flow: {config.get('standardFlowEnabled')}") print(f" - Redirect URIs: {len(config.get('redirectUris', []))} configurées") print(f" - Web Origins: {len(config.get('webOrigins', []))} configurées") # Afficher les redirect URIs print(f" ✓ Redirect URIs configurées:") for uri in config.get('redirectUris', []): print(f" - {uri}") return True else: print(f" ❌ Erreur mise à jour: {response.status_code}") print(f" Réponse: {response.text}") return False except Exception as e: print(f" ❌ Exception: {e}") return False def test_client_after_fix(self, realm_name: str = "unionflow", client_id: str = "unionflow-mobile") -> bool: """Teste le client après correction""" print(f"🧪 Test du client après correction...") # Test d'authentification simple (Resource Owner Password Credentials) 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") return True else: print(f" ❌ Token manquant") else: print(f" ❌ Authentification échouée") print(f" Réponse: {response.text}") except Exception as e: print(f" ❌ Exception: {e}") return False def fix_complete(self): """Correction complète du client""" print("=" * 80) print("🔧 CORRECTION DU CLIENT UNIONFLOW-MOBILE") print("=" * 80) print() if not self.get_admin_token(): print("❌ Impossible d'obtenir le token admin") return False print("✅ Token admin obtenu") print() # Corriger la configuration if not self.fix_client_configuration(): print("❌ Échec de la correction du client") return False print() # Tester le client if self.test_client_after_fix(): print() print("=" * 80) print("🎉 CLIENT CORRIGÉ AVEC SUCCÈS !") print("=" * 80) print() print("🚀 Le client unionflow-mobile est maintenant correctement configuré") print(" pour votre application mobile avec les redirect URIs appropriées.") print() print("📱 REDIRECT URIs CONFIGURÉES :") print(" • http://localhost:* (pour tests locaux)") print(" • http://127.0.0.1:* (pour tests locaux)") print(" • http://192.168.1.11:* (pour votre réseau)") print(" • unionflow://oauth/callback (pour l'app mobile)") print(" • unionflow://login (pour l'app mobile)") print(" • com.unionflow.mobile://oauth (pour l'app mobile)") print(" • urn:ietf:wg:oauth:2.0:oob (pour OAuth out-of-band)") print() print("✅ Votre application mobile peut maintenant s'authentifier !") return True else: print() print("=" * 80) print("⚠️ CLIENT CORRIGÉ MAIS PROBLÈME D'AUTHENTIFICATION") print("=" * 80) print() print("Le client a été reconfiguré mais l'authentification échoue encore.") print("Cela peut être dû aux utilisateurs. Essayez la configuration manuelle :") print() print("1. Ouvrez http://localhost:8180/admin/") print("2. Connectez-vous avec admin/admin") print("3. Realm 'unionflow' > Users > marie.active") print("4. Credentials > Set password > Marie123! (non temporaire)") return False def main(): fixer = ClientRedirectFixer() fixer.fix_complete() if __name__ == "__main__": main()