refresh token ability added to project
This commit is contained in:
@@ -28,7 +28,7 @@ List<SingleChildWidget> get providersRemote {
|
|||||||
|
|
||||||
List<SingleChildWidget> get cores {
|
List<SingleChildWidget> get cores {
|
||||||
return [
|
return [
|
||||||
Provider(create: (context) => NetworkManager()),
|
Provider(create: (context) => NetworkManager(context.read())),
|
||||||
ChangeNotifierProvider(create: (context) => ThemeProvider()),
|
ChangeNotifierProvider(create: (context) => ThemeProvider()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:tm_app/data/services/model/login_response/login_response_model.dart';
|
||||||
|
|
||||||
import '../config/app_config.dart';
|
import '../config/app_config.dart';
|
||||||
import '../utils/logger.dart';
|
import '../utils/logger.dart';
|
||||||
@@ -13,11 +14,14 @@ class NetworkManager {
|
|||||||
final Dio mainDio;
|
final Dio mainDio;
|
||||||
final NetworkConnectivity _connectivity = NetworkConnectivity();
|
final NetworkConnectivity _connectivity = NetworkConnectivity();
|
||||||
bool _interceptorsAdded = false;
|
bool _interceptorsAdded = false;
|
||||||
|
final SharedPreferences _prefs;
|
||||||
|
|
||||||
NetworkManager()
|
NetworkManager(this._prefs)
|
||||||
: mainDio = Dio(
|
: mainDio = Dio(
|
||||||
BaseOptions(
|
BaseOptions(
|
||||||
baseUrl: AppConfig.apiBaseUrl,
|
baseUrl: AppConfig.apiBaseUrl,
|
||||||
|
connectTimeout: const Duration(seconds: 10),
|
||||||
|
receiveTimeout: const Duration(seconds: 10),
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -33,7 +37,10 @@ class NetworkManager {
|
|||||||
final requestInterceptor = InterceptorsWrapper(
|
final requestInterceptor = InterceptorsWrapper(
|
||||||
onRequest: (options, handler) {
|
onRequest: (options, handler) {
|
||||||
appLogger.info('🌐 Request: ${options.method} ${options.path}');
|
appLogger.info('🌐 Request: ${options.method} ${options.path}');
|
||||||
// appLogger.info('🌐 Headers: ${options.headers}');
|
String? token = _prefs.getString('bearer');
|
||||||
|
if (token != null) {
|
||||||
|
options.headers['Authorization'] = 'Bearer $token';
|
||||||
|
}
|
||||||
return handler.next(options);
|
return handler.next(options);
|
||||||
},
|
},
|
||||||
onResponse: (response, handler) {
|
onResponse: (response, handler) {
|
||||||
@@ -43,11 +50,18 @@ class NetworkManager {
|
|||||||
|
|
||||||
return handler.next(response);
|
return handler.next(response);
|
||||||
},
|
},
|
||||||
onError: (error, handler) {
|
onError: (error, handler) async {
|
||||||
appLogger.error(
|
appLogger.error(
|
||||||
'❌ Error: ${error.message} - ${error.requestOptions.path}',
|
'❌ Error: ${error.message} - ${error.requestOptions.path}',
|
||||||
);
|
);
|
||||||
appLogger.error('❌ Error type: ${error.type}');
|
appLogger.error('❌ Error type: ${error.type}');
|
||||||
|
if (error.response?.statusCode == 401) {
|
||||||
|
final newAccessToken = await refreshToken();
|
||||||
|
if (newAccessToken != null) {
|
||||||
|
mainDio.options.headers['Authorization'] = 'Bearer $newAccessToken';
|
||||||
|
return handler.resolve(await mainDio.fetch(error.requestOptions));
|
||||||
|
}
|
||||||
|
}
|
||||||
return handler.next(error);
|
return handler.next(error);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -75,18 +89,12 @@ class NetworkManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
Dio dioInstance = mainDio;
|
Dio dioInstance = mainDio;
|
||||||
|
|
||||||
Future<Response> makeApiCall(String token) async {
|
Future<Response> makeApiCall(String token) async {
|
||||||
final headers =
|
|
||||||
token.isEmpty
|
|
||||||
? <String, String>{}
|
|
||||||
: {'Authorization': 'Bearer $token'};
|
|
||||||
|
|
||||||
final options = Options(
|
final options = Options(
|
||||||
headers: headers,
|
validateStatus:
|
||||||
validateStatus: (status) => status != null && status < 500,
|
(status) => status != null && status >= 200 && status < 300,
|
||||||
);
|
);
|
||||||
|
|
||||||
switch (method.toUpperCase()) {
|
switch (method.toUpperCase()) {
|
||||||
@@ -133,7 +141,7 @@ class NetworkManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String? bearerToken = prefs.getString('bearer');
|
String? bearerToken = _prefs.getString('bearer');
|
||||||
// String? url = prefs.getString('url');
|
// String? url = prefs.getString('url');
|
||||||
|
|
||||||
// appLogger.info('🔗 Base URL: ${url ?? 'NOT SET'}');
|
// appLogger.info('🔗 Base URL: ${url ?? 'NOT SET'}');
|
||||||
@@ -297,4 +305,42 @@ class NetworkManager {
|
|||||||
|
|
||||||
return 'Unknown error';
|
return 'Unknown error';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Refresh the access token using the refresh token
|
||||||
|
Future<String?> refreshToken() async {
|
||||||
|
try {
|
||||||
|
final refreshToken = _prefs.getString('refresh_token');
|
||||||
|
if (refreshToken == null) {
|
||||||
|
appLogger.error('❌ No refresh token available');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = await makeRequest<LoginResponseModel>(
|
||||||
|
'/api/v1/profile/refresh-token',
|
||||||
|
(json) => LoginResponseModel.fromJson(json),
|
||||||
|
method: 'POST',
|
||||||
|
data: {'refresh_token': refreshToken},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result is Ok<LoginResponseModel>) {
|
||||||
|
final loginResponse = result.value;
|
||||||
|
final newAccessToken = loginResponse.data.accessToken;
|
||||||
|
final newRefreshToken = loginResponse.data.refreshToken;
|
||||||
|
|
||||||
|
if (newAccessToken != null) {
|
||||||
|
await _prefs.setString('bearer', newAccessToken);
|
||||||
|
if (newRefreshToken != null) {
|
||||||
|
await _prefs.setString('refresh_token', newRefreshToken);
|
||||||
|
}
|
||||||
|
appLogger.info('✅ Token refreshed successfully');
|
||||||
|
return newAccessToken;
|
||||||
|
}
|
||||||
|
} else if (result is Error<LoginResponseModel>) {
|
||||||
|
appLogger.error('❌ Token refresh failed: ${result.error}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
appLogger.error('❌ Token refresh error: $e');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ class AuthService {
|
|||||||
if (result is Ok<LoginResponseModel>) {
|
if (result is Ok<LoginResponseModel>) {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final token = result.value.data.accessToken;
|
final token = result.value.data.accessToken;
|
||||||
|
final refreshToken = result.value.data.refreshToken;
|
||||||
|
|
||||||
await prefs.setString('bearer', token!);
|
await prefs.setString('bearer', token!);
|
||||||
|
await prefs.setString('refresh_token', refreshToken!);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-2
@@ -3,6 +3,8 @@ import 'dart:ui';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:provider/single_child_widget.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:tm_app/core/config/dependencies.dart';
|
import 'package:tm_app/core/config/dependencies.dart';
|
||||||
import 'package:tm_app/core/theme/theme_provider.dart';
|
import 'package:tm_app/core/theme/theme_provider.dart';
|
||||||
|
|
||||||
@@ -10,15 +12,22 @@ import 'core/routes/app_routes.dart';
|
|||||||
import 'core/theme/colors.dart';
|
import 'core/theme/colors.dart';
|
||||||
import 'core/utils/logger.dart';
|
import 'core/utils/logger.dart';
|
||||||
|
|
||||||
void main() {
|
void main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
// Initialize the logger
|
// Initialize the logger
|
||||||
appLogger.init();
|
appLogger.init();
|
||||||
|
|
||||||
// Log app start
|
// Log app start
|
||||||
appLogger.info('🚀 Starting TM App...');
|
appLogger.info('🚀 Starting TM App...');
|
||||||
// appLogger.logNavigation('Home', 'Login');
|
// appLogger.logNavigation('Home', 'Login');
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
runApp(MultiProvider(providers: [...providersRemote], child: const MyApp()));
|
List<SingleChildWidget> providers = [
|
||||||
|
Provider(create: (context) => prefs),
|
||||||
|
...providersRemote,
|
||||||
|
];
|
||||||
|
|
||||||
|
runApp(MultiProvider(providers: providers, child: const MyApp()));
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatefulWidget {
|
class MyApp extends StatefulWidget {
|
||||||
|
|||||||
Reference in New Issue
Block a user