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:
284
setup-keycloak.sh
Normal file
284
setup-keycloak.sh
Normal file
@@ -0,0 +1,284 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Configuration Keycloak pour UnionFlow
|
||||
# Auteur: UnionFlow Team
|
||||
|
||||
echo "🔐 Configuration automatique de Keycloak pour UnionFlow"
|
||||
echo "======================================================="
|
||||
|
||||
# Variables de configuration
|
||||
KEYCLOAK_URL="http://localhost:8180"
|
||||
ADMIN_USER="admin"
|
||||
ADMIN_PASSWORD="admin"
|
||||
REALM_NAME="unionflow"
|
||||
CLIENT_ID="unionflow-server"
|
||||
CLIENT_SECRET="unionflow-secret-2025"
|
||||
|
||||
# Couleurs pour l'affichage
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Fonction pour obtenir le token d'accès admin
|
||||
get_admin_token() {
|
||||
echo -e "${YELLOW}📡 Obtention du token d'administration...${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_USER&password=$ADMIN_PASSWORD&grant_type=password&client_id=admin-cli")
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)
|
||||
if [ -n "$ACCESS_TOKEN" ]; then
|
||||
echo -e "${GREEN}✅ Token obtenu avec succès${NC}"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "${RED}❌ Erreur lors de l'obtention du token${NC}"
|
||||
echo "Réponse: $TOKEN_RESPONSE"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Fonction pour créer le realm UnionFlow
|
||||
create_realm() {
|
||||
echo -e "${YELLOW}🏛️ Création du realm '$REALM_NAME'...${NC}"
|
||||
|
||||
# Vérifier si le realm existe déjà
|
||||
REALM_CHECK=$(curl -s -X GET "$KEYCLOAK_URL/admin/realms/$REALM_NAME" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-w "%{http_code}")
|
||||
|
||||
if [[ "$REALM_CHECK" == *"200"* ]]; then
|
||||
echo -e "${YELLOW}⚠️ Le realm '$REALM_NAME' existe déjà. Suppression...${NC}"
|
||||
curl -s -X DELETE "$KEYCLOAK_URL/admin/realms/$REALM_NAME" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN"
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# Créer le nouveau realm
|
||||
REALM_CONFIG='{
|
||||
"realm": "'$REALM_NAME'",
|
||||
"displayName": "UnionFlow",
|
||||
"enabled": true,
|
||||
"registrationAllowed": true,
|
||||
"registrationEmailAsUsername": true,
|
||||
"rememberMe": true,
|
||||
"verifyEmail": false,
|
||||
"loginWithEmailAllowed": true,
|
||||
"duplicateEmailsAllowed": false,
|
||||
"resetPasswordAllowed": true,
|
||||
"editUsernameAllowed": false,
|
||||
"sslRequired": "external",
|
||||
"defaultLocale": "fr",
|
||||
"internationalizationEnabled": true,
|
||||
"supportedLocales": ["fr", "en"]
|
||||
}'
|
||||
|
||||
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éé avec succès${NC}"
|
||||
sleep 2
|
||||
else
|
||||
echo -e "${RED}❌ Erreur lors de la création du realm${NC}"
|
||||
echo "Réponse: $RESPONSE"
|
||||
exit 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",
|
||||
"description": "Client pour l API serveur UnionFlow",
|
||||
"enabled": true,
|
||||
"clientAuthenticatorType": "client-secret",
|
||||
"secret": "'$CLIENT_SECRET'",
|
||||
"protocol": "openid-connect",
|
||||
"publicClient": false,
|
||||
"serviceAccountsEnabled": true,
|
||||
"authorizationServicesEnabled": true,
|
||||
"standardFlowEnabled": true,
|
||||
"implicitFlowEnabled": false,
|
||||
"directAccessGrantsEnabled": true,
|
||||
"redirectUris": ["http://localhost:8080/*", "http://localhost:3000/*"],
|
||||
"webOrigins": ["http://localhost:8080", "http://localhost:3000", "*"],
|
||||
"fullScopeAllowed": true,
|
||||
"attributes": {
|
||||
"access.token.lifespan": "3600"
|
||||
}
|
||||
}'
|
||||
|
||||
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éé avec succès${NC}"
|
||||
echo -e "${CYAN}🔑 Secret du client: $CLIENT_SECRET${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ Erreur lors de la création du client${NC}"
|
||||
echo "Réponse: $RESPONSE"
|
||||
exit 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")
|
||||
DESCRIPTIONS=("Administrateur système avec tous les droits"
|
||||
"Président de l'union avec droits de gestion complète"
|
||||
"Secrétaire avec droits de gestion des membres et événements"
|
||||
"Trésorier avec droits de gestion financière"
|
||||
"Gestionnaire des membres avec droits de CRUD sur les membres"
|
||||
"Organisateur d'événements avec droits de gestion des événements"
|
||||
"Membre standard avec droits de consultation")
|
||||
|
||||
for i in "${!ROLES[@]}"; do
|
||||
ROLE_NAME="${ROLES[$i]}"
|
||||
ROLE_DESC="${DESCRIPTIONS[$i]}"
|
||||
|
||||
ROLE_CONFIG='{
|
||||
"name": "'$ROLE_NAME'",
|
||||
"description": "'$ROLE_DESC'"
|
||||
}'
|
||||
|
||||
RESPONSE=$(curl -s -X POST "$KEYCLOAK_URL/admin/realms/$REALM_NAME/roles" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$ROLE_CONFIG" \
|
||||
-w "%{http_code}")
|
||||
|
||||
if [[ "$RESPONSE" == *"201"* ]]; then
|
||||
echo -e " ${GREEN}✅ Rôle '$ROLE_NAME' créé${NC}"
|
||||
else
|
||||
echo -e " ${YELLOW}⚠️ Rôle '$ROLE_NAME' existe déjà ou erreur${NC}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Fonction pour créer un utilisateur
|
||||
create_user() {
|
||||
local username=$1
|
||||
local email=$2
|
||||
local firstname=$3
|
||||
local lastname=$4
|
||||
local password=$5
|
||||
shift 5
|
||||
local roles=("$@")
|
||||
|
||||
USER_CONFIG='{
|
||||
"username": "'$username'",
|
||||
"email": "'$email'",
|
||||
"firstName": "'$firstname'",
|
||||
"lastName": "'$lastname'",
|
||||
"enabled": true,
|
||||
"emailVerified": true,
|
||||
"credentials": [{
|
||||
"type": "password",
|
||||
"value": "'$password'",
|
||||
"temporary": false
|
||||
}]
|
||||
}'
|
||||
|
||||
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 '$username' créé${NC}"
|
||||
|
||||
# Récupérer l'ID de l'utilisateur
|
||||
USER_ID=$(curl -s -X GET "$KEYCLOAK_URL/admin/realms/$REALM_NAME/users?username=$username" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" | \
|
||||
grep -o '"id":"[^"]*' | head -1 | cut -d'"' -f4)
|
||||
|
||||
if [ -n "$USER_ID" ]; then
|
||||
# Assigner les rôles
|
||||
for role in "${roles[@]}"; do
|
||||
# Récupérer les détails du rôle
|
||||
ROLE_DATA=$(curl -s -X GET "$KEYCLOAK_URL/admin/realms/$REALM_NAME/roles/$role" \
|
||||
-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" > /dev/null
|
||||
|
||||
echo -e " ${GREEN}✅ Rôle '$role' assigné à '$username'${NC}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
else
|
||||
echo -e " ${YELLOW}⚠️ Utilisateur '$username' existe déjà ou erreur${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Fonction pour créer les utilisateurs de test
|
||||
create_test_users() {
|
||||
echo -e "${YELLOW}👤 Création des utilisateurs de test...${NC}"
|
||||
|
||||
create_user "admin" "admin@unionflow.dev" "Administrateur" "Système" "admin123" "ADMIN" "PRESIDENT"
|
||||
create_user "president" "president@unionflow.dev" "Jean" "Dupont" "president123" "PRESIDENT" "MEMBRE"
|
||||
create_user "secretaire" "secretaire@unionflow.dev" "Marie" "Martin" "secretaire123" "SECRETAIRE" "GESTIONNAIRE_MEMBRE" "MEMBRE"
|
||||
create_user "tresorier" "tresorier@unionflow.dev" "Pierre" "Durand" "tresorier123" "TRESORIER" "MEMBRE"
|
||||
create_user "membre1" "membre1@unionflow.dev" "Sophie" "Bernard" "membre123" "MEMBRE"
|
||||
}
|
||||
|
||||
# Script principal
|
||||
main() {
|
||||
# Obtenir le token d'administration
|
||||
get_admin_token
|
||||
|
||||
# Créer le realm
|
||||
create_realm
|
||||
|
||||
# Créer le client
|
||||
create_client
|
||||
|
||||
# Créer les rôles
|
||||
create_roles
|
||||
|
||||
# Créer les utilisateurs de test
|
||||
create_test_users
|
||||
|
||||
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}👤 Utilisateurs de test créés :${NC}"
|
||||
echo -e " • admin / admin123 (ADMIN, PRESIDENT)"
|
||||
echo -e " • president / president123 (PRESIDENT, MEMBRE)"
|
||||
echo -e " • secretaire / secretaire123 (SECRETAIRE, GESTIONNAIRE_MEMBRE, MEMBRE)"
|
||||
echo -e " • tresorier / tresorier123 (TRESORIER, MEMBRE)"
|
||||
echo -e " • membre1 / membre123 (MEMBRE)"
|
||||
echo ""
|
||||
echo -e "${YELLOW}🔧 Prochaine étape: Mettre à jour application.properties${NC}"
|
||||
}
|
||||
|
||||
# Exécuter le script principal
|
||||
main
|
||||
Reference in New Issue
Block a user