| README.md | ||
✅ 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
dependencies:
flutter_appauth: ^6.0.0
flutter_secure_storage: ^9.0.0
Codice
import 'package:flutter_appauth/flutter_appauth.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
final FlutterAppAuth appAuth = FlutterAppAuth();
final storage = FlutterSecureStorage();
Future<void> 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
Future<void> 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
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
docker build -t keycloak-local .
docker run -p 8080:8080 keycloak-local
✅ docker-compose.yml
Se vuoi aggiungere persistenza e database (PostgreSQL):
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
#!/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:
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
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
- Dipendenze
Nel pubspec.yaml:
dependencies:
flutter_appauth: ^6.0.0
flutter_secure_storage: ^9.0.0
http: ^1.2.0
- Configurazione Keycloak
Realm: mio-realm
Client: flutter-app
Redirect URI: com.tuaapp:/oauthredirect
Abilita:
standardFlowEnabled=true
directAccessGrantsEnabled=true (per registrazione via API)
- Codice Flutter
Ecco il file auth_service.dart:
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://auth.patachina.it/realms/mio-realm';
final List<String> scopes = ['openid', 'profile', 'email'];
Future<void> 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<void> logout() async {
await _secureStorage.deleteAll();
}
Future<void> 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');
}
}
}
- UI di esempio
Nel main.dart:
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')),
],
),
),
),
);
}
}