import 'package:flutter_appauth/flutter_appauth.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:jwt_decoder/jwt_decoder.dart'; import '../models/user_profile.dart'; class AuthService { static const String _tokenKey = 'auth_token'; static const String _refreshTokenKey = 'refresh_token'; final FlutterAppAuth _appAuth; final FlutterSecureStorage _secureStorage; AuthService({ FlutterAppAuth? appAuth, FlutterSecureStorage? secureStorage, }) : _appAuth = appAuth ?? const FlutterAppAuth(), _secureStorage = secureStorage ?? const FlutterSecureStorage(); Future login() async { try { final result = await _appAuth.authorizeAndExchangeCode( AuthorizationTokenRequest( 'delivery-mobile-app', 'com.goutezplanb.delivery://callback', discoveryUrl: 'https://auth.goutezplanb.com/realms/planb-internal/.well-known/openid-configuration', scopes: const ['openid', 'profile', 'offline_access'], promptValues: const ['login'], ), ); // ignore: unnecessary_null_comparison if (result == null) { return const AuthResult.cancelled(); } await _secureStorage.write(key: _tokenKey, value: result.accessToken ?? ''); if (result.refreshToken != null) { await _secureStorage.write(key: _refreshTokenKey, value: result.refreshToken!); } return AuthResult.success(token: result.accessToken ?? ''); } catch (e) { return AuthResult.error(error: e.toString()); } } Future logout() async { await Future.wait([ _secureStorage.delete(key: _tokenKey), _secureStorage.delete(key: _refreshTokenKey), ]); } Future getToken() async { return await _secureStorage.read(key: _tokenKey); } Future getRefreshToken() async { return await _secureStorage.read(key: _refreshTokenKey); } bool isTokenValid(String? token) { if (token == null || token.isEmpty) return false; try { return !JwtDecoder.isExpired(token); } catch (e) { return false; } } UserProfile? decodeToken(String token) { try { final decodedToken = JwtDecoder.decode(token); return UserProfile.fromJwtClaims(decodedToken); } catch (e) { return null; } } Future isAuthenticated() async { final token = await getToken(); return isTokenValid(token); } } sealed class AuthResult { const AuthResult(); factory AuthResult.success({required String token}) => _Success(token); factory AuthResult.error({required String error}) => _Error(error); const factory AuthResult.cancelled() = _Cancelled; R when({ required R Function(String token) success, required R Function(String error) onError, required R Function() cancelled, }) { return switch (this) { _Success(:final token) => success(token), _Error(:final error) => onError(error), _Cancelled() => cancelled(), }; } } final class _Success extends AuthResult { final String token; const _Success(this.token); } final class _Error extends AuthResult { final String error; const _Error(this.error); } final class _Cancelled extends AuthResult { const _Cancelled(); }