From 4d4689132301feef896f0e666fa2f73db55b68ce Mon Sep 17 00:00:00 2001 From: Fabio Date: Wed, 12 Nov 2025 10:14:13 +0100 Subject: [PATCH] first commit --- README.md | 382 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..add6ac9 --- /dev/null +++ b/README.md @@ -0,0 +1,382 @@ +# ✅ Parte 1: Guida completa per installare e configurare Keycloak come server OAuth2/OIDC locale + +## Step 1: Scarica e avvia Keycloak + +Vai su (https://www.keycloak.org/downloads) + +Scarica la versione Quarkus distribution. + +Avvia in modalità sviluppo: + + ./bin/kc.sh start-dev + +Keycloak sarà disponibile su: + + http://localhost:8080 + +## Step 2: Configura Realm e Client + +Accedi alla console admin: + + http://localhost:8080/admin. + +Crea un Realm (es. flutter-realm). + +Crea un Client: + +Tipo: Public + +Nome: flutter-app + +Redirect URI: + + com.tuaapp:/oauthredirect + + +Abilita Standard Flow (Authorization Code). + +Salva il Client ID. + +## Step 3: Configura utenti + +Vai su Users → Aggiungi utente → Imposta credenziali. + +Ora hai un utente locale per il login. + + +## Step 4: Sicurezza + +In locale puoi usare HTTP, ma in produzione serve HTTPS. + +Abilita PKCE per sicurezza (Keycloak lo supporta di default). + +# ✅ Parte 2: Esempio Flutter che si collega a Keycloak locale + +Dipendenze + +```sh +dependencies: + flutter_appauth: ^6.0.0 + flutter_secure_storage: ^9.0.0 +``` + +Codice + +```sh +import 'package:flutter_appauth/flutter_appauth.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +final FlutterAppAuth appAuth = FlutterAppAuth(); +final storage = FlutterSecureStorage(); + +Future login() async { + final AuthorizationTokenResponse? result = await appAuth.authorizeAndExchangeCode( + AuthorizationTokenRequest( + 'flutter-app', // Client ID + 'com.tuaapp:/oauthredirect', // Redirect URI + issuer: 'http://localhost:8080/realms/flutter-realm', + scopes: ['openid', 'profile', 'email'], + ), + ); + + if (result != null) { + await storage.write(key: 'access_token', value: result.accessToken); + await storage.write(key: 'id_token', value: result.idToken); + print('Login OK: ${result.accessToken}'); + } +} +``` + +Refresh Token +```sh +Future refreshToken() async { + final refreshToken = await storage.read(key: 'refresh_token'); + if (refreshToken != null) { + final TokenResponse? response = await appAuth.token( + TokenRequest( + 'flutter-app', + 'com.tuaapp:/oauthredirect', + refreshToken: refreshToken, + issuer: 'http://localhost:8080/realms/flutter-realm', + ), + ); + if (response != null) { + await storage.write(key: 'access_token', value: response.accessToken); + } + } +} +``` + + +# ✅ Dockerfile per Keycloak +Questo crea un container con Keycloak in modalità sviluppo: + +Dockerfile +```sh +FROM quay.io/keycloak/keycloak:24.0.4 + +# Imposta la modalità sviluppo +ENV KC_DB=dev-mem +ENV KC_HTTP_PORT=8080 +ENV KC_HOSTNAME=localhost + +# Avvia Keycloak in modalità sviluppo +``` +Build e Run + +```sh +docker build -t keycloak-local . +docker run -p 8080:8080 keycloak-local +``` + +## ✅ docker-compose.yml +Se vuoi aggiungere persistenza e database (PostgreSQL): + +```sh +version: '3.8' +services: + keycloak: + image: quay.io/keycloak/keycloak:24.0.4 + container_name: keycloak + environment: + KC_DB: postgres + KC_DB_URL_HOST: postgres + KC_DB_URL_DATABASE: keycloak + KC_DB_USERNAME: keycloak + KC_DB_PASSWORD: keycloak + KC_HOSTNAME: localhost + ports: + - "8080:8080" + command: + - start-dev + depends_on: + - postgres + + postgres: + image: postgres:15 + container_name: postgres + environment: + POSTGRES_DB: keycloak + POSTGRES_USER: keycloak + POSTGRES_PASSWORD: keycloak + ports: + - "5432:5432" +``` + +## ✅ Come usarlo + +Salva il file come docker-compose.yml. + +e avvia con + + docker-compose up -d + +Accedi a Keycloak su http://localhost:8080. + +Crea Realm, Client, e imposta il Redirect URI per la tua app Flutter. + + +## ✅ Script Bash: init-keycloak.sh + +```sh +#!/bin/bash + +# Variabili +KEYCLOAK_URL="http://localhost:8080" +REALM_NAME="mio-realm" +CLIENT_ID="flutter-app" +REDIRECT_URI="com.tuaapp:/oauthredirect" +ADMIN_USER="admin" +ADMIN_PASS="admin" + +# Login come admin +/opt/keycloak/bin/kcadm.sh config credentials \ + --server $KEYCLOAK_URL \ + --realm master \ + --user $ADMIN_USER \ + --password $ADMIN_PASS + +# Crea Realm +/opt/keycloak/bin/kcadm.sh create realms \ + -s realm=$REALM_NAME \ + -s enabled=true + +# Crea Client +/opt/keycloak/bin/kcadm.sh create clients -r $REALM_NAME \ + -s clientId=$CLIENT_ID \ + -s enabled=true \ + -s publicClient=true \ + -s redirectUris="[\"$REDIRECT_URI\"]" \ + -s protocol=openid-connect \ + -s standardFlowEnabled=true \ + -s directAccessGrantsEnabled=true + +echo "✅ Realm '$REALM_NAME' e Client '$CLIENT_ID' creati con successo!" +``` +### ✅ Come usarlo con Docker + +Copia lo script in una cartella. + +Aggiorna docker-compose.yml aggiungendo un volume per montare lo script: + +```sh +volumes: + - ./init-keycloak.sh:/opt/keycloak/init-keycloak.sh +``` + +Dopo che il container è avviato, esegui: + + docker exec -it keycloak bash /opt/keycloak/init-keycloak.sh + +## Nginx config + +```sh +server { + listen 443 ssl; + server_name keycloak.local; + + ssl_certificate /etc/nginx/ssl/keycloak.crt; + ssl_certificate_key /etc/nginx/ssl/keycloak.key; + + location / { + proxy_pass http://keycloak:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + } +} + +server { + listen 80; + server_name keycloak.local; + return 301 https://$host$request_uri; +} +``` + +## Flutter + +1. Dipendenze + +Nel pubspec.yaml: + +```sh +dependencies: + flutter_appauth: ^6.0.0 + flutter_secure_storage: ^9.0.0 + http: ^1.2.0 +``` + +2. Configurazione Keycloak + +Realm: mio-realm + +Client: flutter-app + +Redirect URI: com.tuaapp:/oauthredirect + +Abilita: + +standardFlowEnabled=true + +directAccessGrantsEnabled=true (per registrazione via API) + +3. Codice Flutter + +Ecco il file auth_service.dart: + +```sh +import 'package:flutter_appauth/flutter_appauth.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; + +class AuthService { + final FlutterAppAuth _appAuth = FlutterAppAuth(); + final FlutterSecureStorage _secureStorage = const FlutterSecureStorage(); + + final String clientId = 'flutter-app'; + final String redirectUri = 'com.tuaapp:/oauthredirect'; + final String issuer = 'https://keycloak.local/realms/mio-realm'; + final List scopes = ['openid', 'profile', 'email']; + + Future login() async { + final result = await _appAuth.authorizeAndExchangeCode( + AuthorizationTokenRequest( + clientId, + redirectUri, + issuer: issuer, + scopes: scopes, + ), + ); + + if (result != null) { + await _secureStorage.write(key: 'access_token', value: result.accessToken); + await _secureStorage.write(key: 'refresh_token', value: result.refreshToken); + await _secureStorage.write(key: 'id_token', value: result.idToken); + } + } + + Future logout() async { + await _secureStorage.deleteAll(); + } + + Future register(String username, String password, String email) async { + final url = '$issuer/protocol/openid-connect/token'; + // Usa le API Admin di Keycloak o un endpoint custom + // Qui esempio con Direct Access Grant (non consigliato in produzione) + final response = await http.post( + Uri.parse('$issuer/protocol/openid-connect/token'), + headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + body: { + 'client_id': clientId, + 'grant_type': 'password', + 'username': username, + 'password': password, + }, + ); + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + await _secureStorage.write(key: 'access_token', value: data['access_token']); + await _secureStorage.write(key: 'refresh_token', value: data['refresh_token']); + } else { + throw Exception('Errore registrazione/login'); + } + } +} +``` + +4. UI di esempio + +Nel main.dart: + +```sh +import 'package:flutter/material.dart'; +import 'auth_service.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatelessWidget { + final AuthService authService = AuthService(); + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: Text('Login & Registrazione')), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton(onPressed: authService.login, child: Text('Login')), + ElevatedButton(onPressed: () => authService.register('user', 'pass', 'email@test.com'), child: Text('Registrati')), + ElevatedButton(onPressed: authService.logout, child: Text('Logout')), + ], + ), + ), + ), + ); + } +} +``` +