feat(auth): gestion reAuthRequired + suppression flux changement mot de passe manuel
- AuthStatusResult: nouveau champ reAuthRequired (ancien compte nécessitant UPDATE_PASSWORD) - AuthBloc._onLoginRequested: si reAuthRequired → logout silencieux + re-déclenchement AppAuth automatique (Keycloak affiche l'écran de changement de mot de passe dans Chrome Custom Tab) - AuthBloc._onStatusChecked: si reAuthRequired → logout + AuthUnauthenticated (redirection login) - Remplacement du flux premierLoginComplet (boolean) par enum côté backend - Suppression de AuthPasswordChangeRequired, AuthPasswordChanging, change_password_page.dart
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_appauth/flutter_appauth.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:jwt_decoder/jwt_decoder.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
@@ -22,6 +23,7 @@ class KeycloakConfig {
|
||||
@lazySingleton
|
||||
class KeycloakAuthService {
|
||||
final Dio _dio = Dio();
|
||||
final FlutterAppAuth _appAuth = const FlutterAppAuth();
|
||||
final FlutterSecureStorage _storage = const FlutterSecureStorage(
|
||||
aOptions: AndroidOptions(encryptedSharedPreferences: true),
|
||||
iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock_this_device),
|
||||
@@ -31,23 +33,28 @@ class KeycloakAuthService {
|
||||
static const String _refreshK = 'kc_refresh';
|
||||
static const String _idK = 'kc_id';
|
||||
|
||||
/// Login via Direct Access Grant (Username/Password)
|
||||
Future<User?> login(String username, String password) async {
|
||||
/// Login via Authorization Code Flow + PKCE (AppAuth)
|
||||
Future<User?> loginWithAppAuth() async {
|
||||
try {
|
||||
final response = await _dio.post(
|
||||
KeycloakConfig.tokenEndpoint,
|
||||
data: {
|
||||
'client_id': KeycloakConfig.clientId,
|
||||
'grant_type': 'password',
|
||||
'username': username,
|
||||
'password': password,
|
||||
'scope': KeycloakConfig.scopes,
|
||||
},
|
||||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||
final result = await _appAuth.authorizeAndExchangeCode(
|
||||
AuthorizationTokenRequest(
|
||||
KeycloakConfig.clientId,
|
||||
'dev.lions.unionflow-mobile://auth/callback',
|
||||
serviceConfiguration: AuthorizationServiceConfiguration(
|
||||
authorizationEndpoint: '${KeycloakConfig.baseUrl}/realms/${KeycloakConfig.realm}/protocol/openid-connect/auth',
|
||||
tokenEndpoint: KeycloakConfig.tokenEndpoint,
|
||||
),
|
||||
scopes: ['openid', 'profile', 'email', 'roles', 'offline_access'],
|
||||
additionalParameters: {'kc_locale': 'fr'},
|
||||
allowInsecureConnections: true,
|
||||
),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
await _saveTokens(response.data);
|
||||
if (result?.accessToken != null) {
|
||||
await _saveTokens({
|
||||
'access_token': result!.accessToken,
|
||||
'refresh_token': result.refreshToken,
|
||||
'id_token': result.idToken,
|
||||
});
|
||||
return await getCurrentUser();
|
||||
}
|
||||
} catch (e, st) {
|
||||
@@ -202,6 +209,9 @@ class KeycloakAuthService {
|
||||
souscriptionId: data['souscriptionId'] as String?,
|
||||
waveSessionId: data['waveSessionId'] as String?,
|
||||
organisationId: data['organisationId'] as String?,
|
||||
typeOrganisation: data['typeOrganisation'] as String?,
|
||||
premierLoginComplet: (data['premierLoginComplet'] as bool?) ?? false,
|
||||
reAuthRequired: (data['reAuthRequired'] as bool?) ?? false,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -218,6 +228,11 @@ class AuthStatusResult {
|
||||
final String? souscriptionId;
|
||||
final String? waveSessionId;
|
||||
final String? organisationId;
|
||||
final String? typeOrganisation;
|
||||
/// true si le premier login vient d'être complété (token à rafraîchir pour avoir MEMBRE/MEMBRE_ACTIF)
|
||||
final bool premierLoginComplet;
|
||||
/// true si une réauthentification est requise (UPDATE_PASSWORD vient d'être assigné sur un ancien compte)
|
||||
final bool reAuthRequired;
|
||||
|
||||
const AuthStatusResult({
|
||||
required this.statutCompte,
|
||||
@@ -225,6 +240,9 @@ class AuthStatusResult {
|
||||
this.souscriptionId,
|
||||
this.waveSessionId,
|
||||
this.organisationId,
|
||||
this.typeOrganisation,
|
||||
this.premierLoginComplet = false,
|
||||
this.reAuthRequired = false,
|
||||
});
|
||||
|
||||
bool get isActive => statutCompte == 'ACTIF';
|
||||
|
||||
Reference in New Issue
Block a user