feat(mobile): Implement Keycloak WebView authentication with HTTP callback
- Replace flutter_appauth with custom WebView implementation to resolve deep link issues - Add KeycloakWebViewAuthService with integrated WebView for seamless authentication - Configure Android manifest for HTTP cleartext traffic support - Add network security config for development environment (192.168.1.11) - Update Keycloak client to use HTTP callback endpoint (http://192.168.1.11:8080/auth/callback) - Remove obsolete keycloak_auth_service.dart and temporary scripts - Clean up dependencies and regenerate injection configuration - Tested successfully on multiple Android devices (Xiaomi 2201116TG, SM A725F) BREAKING CHANGE: Authentication flow now uses WebView instead of external browser - Users will see Keycloak login page within the app instead of browser redirect - Resolves ERR_CLEARTEXT_NOT_PERMITTED and deep link state management issues - Maintains full OIDC compliance with PKCE flow and secure token storage Technical improvements: - WebView with custom navigation delegate for callback handling - Automatic token extraction and user info parsing from JWT - Proper error handling and user feedback - Consistent authentication state management across app lifecycle
This commit is contained in:
298
complete-keycloak-setup.sh
Normal file
298
complete-keycloak-setup.sh
Normal file
@@ -0,0 +1,298 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Configuration complète de Keycloak pour UnionFlow
|
||||
echo "🔐 Configuration complète de Keycloak pour UnionFlow"
|
||||
echo "===================================================="
|
||||
|
||||
# Variables
|
||||
KEYCLOAK_URL="http://localhost:8180"
|
||||
REALM_NAME="unionflow"
|
||||
CLIENT_ID="unionflow-server"
|
||||
CLIENT_SECRET="unionflow-secret-2025"
|
||||
|
||||
# Couleurs
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Fonction pour obtenir un token admin
|
||||
get_admin_token() {
|
||||
echo -e "${YELLOW}📡 Obtention du token admin...${NC}"
|
||||
|
||||
TOKEN_RESPONSE=$(curl -s -X POST "$KEYCLOAK_URL/realms/master/protocol/openid-connect/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "username=admin&password=admin&grant_type=password&client_id=admin-cli")
|
||||
|
||||
ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)
|
||||
|
||||
if [ -n "$ACCESS_TOKEN" ]; then
|
||||
echo -e "${GREEN}✅ Token admin obtenu${NC}"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ Impossible d'obtenir le token admin${NC}"
|
||||
echo "Réponse: $TOKEN_RESPONSE"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Fonction pour supprimer et recréer le realm
|
||||
recreate_realm() {
|
||||
echo -e "${YELLOW}🏛️ Suppression et recréation du realm '$REALM_NAME'...${NC}"
|
||||
|
||||
# Supprimer le realm s'il existe
|
||||
curl -s -X DELETE "$KEYCLOAK_URL/admin/realms/$REALM_NAME" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" > /dev/null
|
||||
|
||||
sleep 2
|
||||
|
||||
# Créer le nouveau realm
|
||||
REALM_CONFIG='{
|
||||
"realm": "'$REALM_NAME'",
|
||||
"displayName": "UnionFlow",
|
||||
"enabled": true,
|
||||
"registrationAllowed": false,
|
||||
"registrationEmailAsUsername": true,
|
||||
"rememberMe": true,
|
||||
"verifyEmail": false,
|
||||
"loginWithEmailAllowed": true,
|
||||
"duplicateEmailsAllowed": false,
|
||||
"resetPasswordAllowed": true,
|
||||
"editUsernameAllowed": false,
|
||||
"sslRequired": "external",
|
||||
"defaultLocale": "fr"
|
||||
}'
|
||||
|
||||
RESPONSE=$(curl -s -X POST "$KEYCLOAK_URL/admin/realms" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$REALM_CONFIG" \
|
||||
-w "%{http_code}")
|
||||
|
||||
if [[ "$RESPONSE" == *"201"* ]]; then
|
||||
echo -e "${GREEN}✅ Realm '$REALM_NAME' créé${NC}"
|
||||
sleep 2
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ Erreur lors de la création du realm${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Fonction pour créer le client
|
||||
create_client() {
|
||||
echo -e "${YELLOW}🔧 Création du client '$CLIENT_ID'...${NC}"
|
||||
|
||||
CLIENT_CONFIG='{
|
||||
"clientId": "'$CLIENT_ID'",
|
||||
"name": "UnionFlow Server API",
|
||||
"enabled": true,
|
||||
"clientAuthenticatorType": "client-secret",
|
||||
"secret": "'$CLIENT_SECRET'",
|
||||
"protocol": "openid-connect",
|
||||
"publicClient": false,
|
||||
"serviceAccountsEnabled": true,
|
||||
"standardFlowEnabled": true,
|
||||
"implicitFlowEnabled": false,
|
||||
"directAccessGrantsEnabled": true,
|
||||
"authorizationServicesEnabled": false,
|
||||
"redirectUris": ["http://localhost:8080/*"],
|
||||
"webOrigins": ["http://localhost:8080", "*"],
|
||||
"fullScopeAllowed": true
|
||||
}'
|
||||
|
||||
RESPONSE=$(curl -s -X POST "$KEYCLOAK_URL/admin/realms/$REALM_NAME/clients" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$CLIENT_CONFIG" \
|
||||
-w "%{http_code}")
|
||||
|
||||
if [[ "$RESPONSE" == *"201"* ]]; then
|
||||
echo -e "${GREEN}✅ Client '$CLIENT_ID' créé${NC}"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ Erreur lors de la création du client${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Fonction pour créer les rôles
|
||||
create_roles() {
|
||||
echo -e "${YELLOW}👥 Création des rôles...${NC}"
|
||||
|
||||
ROLES=("ADMIN" "PRESIDENT" "SECRETAIRE" "TRESORIER" "GESTIONNAIRE_MEMBRE" "ORGANISATEUR_EVENEMENT" "MEMBRE")
|
||||
|
||||
for ROLE_NAME in "${ROLES[@]}"; do
|
||||
ROLE_CONFIG='{
|
||||
"name": "'$ROLE_NAME'",
|
||||
"description": "Rôle '$ROLE_NAME' pour UnionFlow"
|
||||
}'
|
||||
|
||||
curl -s -X POST "$KEYCLOAK_URL/admin/realms/$REALM_NAME/roles" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$ROLE_CONFIG" > /dev/null
|
||||
|
||||
echo -e " ${GREEN}✅ Rôle '$ROLE_NAME' créé${NC}"
|
||||
done
|
||||
}
|
||||
|
||||
# Fonction pour créer un utilisateur de test
|
||||
create_test_user() {
|
||||
echo -e "${YELLOW}👤 Création de l'utilisateur de test...${NC}"
|
||||
|
||||
USER_CONFIG='{
|
||||
"username": "testuser",
|
||||
"email": "test@unionflow.dev",
|
||||
"firstName": "Test",
|
||||
"lastName": "User",
|
||||
"enabled": true,
|
||||
"emailVerified": true
|
||||
}'
|
||||
|
||||
RESPONSE=$(curl -s -X POST "$KEYCLOAK_URL/admin/realms/$REALM_NAME/users" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$USER_CONFIG" \
|
||||
-w "%{http_code}")
|
||||
|
||||
if [[ "$RESPONSE" == *"201"* ]]; then
|
||||
echo -e "${GREEN}✅ Utilisateur créé${NC}"
|
||||
|
||||
# Récupérer l'ID de l'utilisateur
|
||||
USER_ID=$(curl -s -X GET "$KEYCLOAK_URL/admin/realms/$REALM_NAME/users?username=testuser" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" | \
|
||||
grep -o '"id":"[^"]*' | head -1 | cut -d'"' -f4)
|
||||
|
||||
if [ -n "$USER_ID" ]; then
|
||||
# Définir le mot de passe
|
||||
PASSWORD_CONFIG='{
|
||||
"type": "password",
|
||||
"value": "test123",
|
||||
"temporary": false
|
||||
}'
|
||||
|
||||
curl -s -X PUT "$KEYCLOAK_URL/admin/realms/$REALM_NAME/users/$USER_ID/reset-password" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$PASSWORD_CONFIG"
|
||||
|
||||
echo -e "${GREEN}✅ Mot de passe défini${NC}"
|
||||
|
||||
# Assigner le rôle MEMBRE
|
||||
ROLE_DATA=$(curl -s -X GET "$KEYCLOAK_URL/admin/realms/$REALM_NAME/roles/MEMBRE" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN")
|
||||
|
||||
if [[ "$ROLE_DATA" == *'"name"'* ]]; then
|
||||
ROLE_ASSIGNMENT="[$ROLE_DATA]"
|
||||
|
||||
curl -s -X POST "$KEYCLOAK_URL/admin/realms/$REALM_NAME/users/$USER_ID/role-mappings/realm" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$ROLE_ASSIGNMENT"
|
||||
|
||||
echo -e "${GREEN}✅ Rôle MEMBRE assigné${NC}"
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ Erreur lors de la création de l'utilisateur${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Fonction pour tester l'authentification
|
||||
test_authentication() {
|
||||
echo -e "${YELLOW}🧪 Test d'authentification...${NC}"
|
||||
|
||||
AUTH_RESPONSE=$(curl -s -X POST "$KEYCLOAK_URL/realms/$REALM_NAME/protocol/openid-connect/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "username=testuser&password=test123&grant_type=password&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET")
|
||||
|
||||
AUTH_TOKEN=$(echo $AUTH_RESPONSE | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)
|
||||
|
||||
if [ -n "$AUTH_TOKEN" ]; then
|
||||
echo -e "${GREEN}✅ Authentification réussie !${NC}"
|
||||
echo -e "${CYAN}🔑 Token obtenu (tronqué): ${AUTH_TOKEN:0:50}...${NC}"
|
||||
|
||||
# Test d'accès à l'API
|
||||
echo -e "${YELLOW}🧪 Test d'accès à l'API UnionFlow...${NC}"
|
||||
API_RESPONSE=$(curl -s -w "%{http_code}" -H "Authorization: Bearer $AUTH_TOKEN" "http://localhost:8080/api/organisations")
|
||||
HTTP_CODE=$(echo "$API_RESPONSE" | tail -c 4)
|
||||
|
||||
if [ "$HTTP_CODE" = "200" ]; then
|
||||
echo -e "${GREEN}✅ Accès API réussi !${NC}"
|
||||
elif [ "$HTTP_CODE" = "403" ]; then
|
||||
echo -e "${YELLOW}⚠️ Accès refusé - Permissions insuffisantes (normal pour un utilisateur MEMBRE)${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Code de réponse: $HTTP_CODE${NC}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ Échec de l'authentification${NC}"
|
||||
echo "Réponse: $AUTH_RESPONSE"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Script principal
|
||||
main() {
|
||||
echo -e "${CYAN}🚀 Démarrage de la configuration complète...${NC}"
|
||||
echo ""
|
||||
|
||||
# Obtenir le token admin
|
||||
if ! get_admin_token; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Recréer le realm
|
||||
if ! recreate_realm; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Créer le client
|
||||
if ! create_client; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Créer les rôles
|
||||
create_roles
|
||||
|
||||
# Créer l'utilisateur de test
|
||||
if ! create_test_user; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Tester l'authentification
|
||||
if test_authentication; then
|
||||
echo ""
|
||||
echo -e "${GREEN}🎉 CONFIGURATION KEYCLOAK TERMINÉE AVEC SUCCÈS !${NC}"
|
||||
echo -e "${GREEN}===============================================${NC}"
|
||||
echo -e "${CYAN}📋 Informations de configuration :${NC}"
|
||||
echo -e " • Realm: $REALM_NAME"
|
||||
echo -e " • Client ID: $CLIENT_ID"
|
||||
echo -e " • Client Secret: $CLIENT_SECRET"
|
||||
echo -e " • URL Auth Server: $KEYCLOAK_URL/realms/$REALM_NAME"
|
||||
echo ""
|
||||
echo -e "${CYAN}👤 Utilisateur de test :${NC}"
|
||||
echo -e " • Username: testuser"
|
||||
echo -e " • Password: test123"
|
||||
echo -e " • Rôle: MEMBRE"
|
||||
echo ""
|
||||
echo -e "${CYAN}🔗 URLs importantes :${NC}"
|
||||
echo -e " • UnionFlow API: http://localhost:8080"
|
||||
echo -e " • Swagger UI: http://localhost:8080/q/swagger-ui"
|
||||
echo -e " • Health Check: http://localhost:8080/health"
|
||||
echo -e " • Keycloak Admin: http://localhost:8180/admin"
|
||||
echo ""
|
||||
echo -e "${GREEN}✅ L'intégration Keycloak avec UnionFlow est maintenant fonctionnelle !${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ Échec de la configuration${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Exécuter le script principal
|
||||
main
|
||||
Reference in New Issue
Block a user