Flutter while refreshToken getting old refresh token - android

I making refresh token with dio interceptors. Its working well but while i have multiple request in same time its trying using old refresh token. What can i do for fix it ? I tried use QueuedInterceptorsWrapper too but it has same result. Let me show you my codes first :
My Interceptor on constructor.
_dio.interceptors.add(
InterceptorsWrapper(onResponse: (Response<dynamic> response, ResponseInterceptorHandler handler) {
return handler.next(response);
}, onError: (DioError error, ErrorInterceptorHandler handler) async {
if (error.response?.statusCode == 401) {
var options = error.response!.requestOptions;
if ("Bearer $accessToken" != options.headers[HttpHeaders.authorizationHeader]) {
options.headers[HttpHeaders.authorizationHeader] = "Bearer $accessToken";
_dio.fetch(options).then((value) => handler.resolve(value), onError: (err) {
log("error on fetchiN!!!!");
});
// log("$accessToken ////// ${_dio.options.headers["Authorization"]}");
}
final refToken = await UserSecureStorage.getField("refreshToken");
if (refToken != null) {
log("hata alındı ${error.requestOptions.path}");
await refreshToken(refToken);
return handler.resolve(await _retry(error.requestOptions));
}
}
return handler.next(error);
}, onRequest: (RequestOptions options, RequestInterceptorHandler handler) async {
log("request atıldı ${options.path} ---Ref token => ${await UserSecureStorage.getField("refreshToken")}");
if (accessToken == null) {
accessToken = await UserSecureStorage.getField("token");
}
options.headers["Authorization"] = "Bearer $accessToken";
return handler.next(options);
}),
);
Refresh token function
Future<void> refreshToken(String? refreshToken) async {
final refToken = await UserSecureStorage.getField("refreshToken");
//In first request its changing and its working fine. But while it have multiple request in same time while in second request its trying use old refreshtoken
//token dio is another instance of dio so it hasnt any interceptor.Its only has baseurl.
final response = await tokenDio.get("/Auth/RefreshTokenLogin?
refreshToken=${Uri.encodeQueryComponent(refToken!)}");
if (response.statusCode == 200) {
final RefreshTokenResponseModel? model =
RefreshTokenResponseModel.fromJson(response.data);
accessToken = model!.data!.token;
log("refreshlendi! Yenisi => ${model.data!.refreshToken}");
await UserSecureStorage.setField("token", model.data!.token);
await UserSecureStorage.setField("refreshToken", model.data!.refreshToken);
_dio.options.headers[HttpHeaders.authorizationHeader] = "Bearer ${model.data!.token}";
}
}
Some screenshoots for make it clear.
Request sent to notification.
Request sent to therapist
Get error on notification
Request sent to PurchasedPackages
Get error on PurchasedPackages
Token refreshed. New token +pVX....
Error on another instance of dio for refresh token without any interceptor.
Refreshtoken instance tried refresh it with old refresh token.Ke.... So its gaving DioError on refreshtoken instance.
What can i do for fix it ? Thanks for response!

Related

Flutter BloC repeat an event again after dio request time out

I have a problem on my flutter app, when try to load a data from api using dio and this api is private so need to be connected to the same network, so to check everything is fine I tried to connect using mobile data that means dio connection won't success and return timeout, when I call that inside BLoC and use BloCBuilder to build UI depending on state bloc return loadingstate then return errorstate and try to do the event again and fail then repeat this over and over, I just want to avoid this and return error state only and stop listening on that event
void _loadAllSpecialities(
LoadAllSpecialities event, Emitter<DoctorsState> emit) async {
emit(
const DoctorsLoadingState(),
);
emit(const DoctorsLoadingState());
final result = await doctorService.getAllSpeciality(event.jwtToken);
print(result.toString());
//has no error and data loaded
if (result.item1 == null) {
final speicailities = result.item2;
emit(DoctorsSpecialitiesLoaded(specialities: speicailities));
} else {
//has error (error not null)
emit(DoctorErrorState(result.item1!));
}
}```
.
class DoctorService {
final List<DoctorSpeciality> specialities = [];
final options = Options(
responseType: ResponseType.json,
receiveTimeout: 2000,
sendTimeout: 2000,
);
final _dio = Dio();
Future<Tuple<String?, List<DoctorSpeciality>>> getAllSpeciality(
String jwtToken) async {
specialities.clear();
var tuple = Tuple<String?, List<DoctorSpeciality>>(null, []);
try {
final response = await _dio.get<List>(ApiVars.specialitiesEndPoint,
options:
options.copyWith(headers: {"Authorization": "Bearer $jwtToken"}));
if (response.statusCode == 200) {
//has no data
if (response.data == null) {
//set error 1
tuple.setNewValues('No data loaded', []);
//print it
log(tuple.item1 ?? '');
//return tuple with error and empty list
return tuple;
}
//has data then map it into list of specialities
response.data?.forEach((element) {
//convert json to speciality and add it to specialities list
specialities.add(DoctorSpeciality.fromJson(element));
});
//set error to null and list to specialites list
tuple.setNewValues(null, specialities);
return tuple;
} else {
//set error to error with the code and list to empty list
tuple.setNewValues('error occur with code ${response.statusCode}', []);
log(tuple.item1 ?? '');
return tuple;
}
} on DioError catch (error) {
//set error to error message and list to empty list
tuple.setNewValues("doc service ${error.message}", []);
log(tuple.item1 ?? '');
return tuple;
}
}
}
I tried add droppable, sequential and didn't work
on<LoadAllSpecialities>(_loadAllSpecialities, transformer: droppable());
I solved the problem by adding Future before function that called inside the handler function and await it to end as code in below
///constructor called super and pass initial state...
on<DoctorsEvent>(
(event, emit) async {
try {
if (event is LoadAllSpecialities) {
// * load all specialities of doctors from api ...
//add await here
await _loadAllSpecialities(event, emit);
}
} catch (error) {
emit(DoctorErrorState(error.toString()));
}
},
);
}
//add future here
Future<void> _loadAllSpecialities(
LoadAllSpecialities event, Emitter<DoctorsState> emit) async {
emit(
const DoctorsLoadingState(),
);
emit(const DoctorsLoadingState());
final result = await doctorService.getAllSpeciality(event.jwtToken);
//has no error and data loaded
if (result.item1 == null) {
final speicailities = result.item2;
emit(DoctorsSpecialitiesLoaded(specialities: speicailities));
} else {
//has error (error not null)
emit(DoctorErrorState(result.item1!));
}
}

Error 403 "The caller does not have permission"

I am trying to fetch data from dynamic links stastics API, and I have already sent bearer token inside the header. But it shows the error of permission denied.
final response = await http
.get(Uri.parse("https://patelmatch-api.herokuapp.com/getStat"));
final encodedLink = Uri.encodeComponent(currentAppuser.value.url!);
if (response.statusCode == 200) {
LinkStat linkStat = LinkStat.fromJson(jsonDecode(response.body));
print(linkStat.accessToken);
final responseData = await http.get(
Uri.parse(
"https://firebasedynamiclinks.googleapis.com/v1/${encodedLink}/linkStats?durationDays=7"),
headers: {'Authorization': 'Bearer ' + linkStat.accessToken!});
print(responseData.body);
} else {
print("Error");
}
isLoading.toggle();
index.js
const express = require('express')
const cors = require('cors')
const app = express()
app.use(express.json())
app.use(cors())
var { google } = require("googleapis");
const port = process.env.PORT || 5000
// Load the service account key JSON file.
var serviceAccount = require("./path/to/serviceAccountKey.json");
// Specify the required scope.
var scopes = [
"https://www.googleapis.com/auth/firebase"
];
app.get('/getStat', async(req, res) => {
var jwtClient = new google.auth.JWT(
serviceAccount.client_email,
null,
serviceAccount.private_key,
scopes
);
// Use the JWT client to generate an access token.
jwtClient.authorize(function(error, tokens) {
if (error) {
console.log("Error making request to generate access token:", error);
} else if (tokens.access_token === null) {
console.log("Provided service account does not have permission to generate access tokens");
} else {
var accessToken = tokens.access_token;
console.log(tokens.access_token)
console.log(accessToken.length)
res.send({accessToken});
// Include the access token in the Authorization header.
}
});
})
app.listen(port, () => console.log("up & running on 4000"))
Here I have generated access token from nodejs and fetching data from flutter, but I am having error of permission. How to solve this issue?

How to cache the SSL Handshake and other Network elements in Flutter?

I am developing a flutter application with network activities. To get data, I am connecting to a REST API which is pretty fast. You can see the postman performance of an API call below.
Then, when I execute the same API in flutter, this is what I got.
If you see, the secret is that postman cached the DNS Lookup, TCP Handshake and SSL Handshake. But in flutter the "Connection established" time is high, which I believe it does not do any caching.
Below is my code, connecting to network.
LoadingScreen2Controller
class LoadingScreen2Controller {
Future<void> initProcess(BuildContext context) async {
final FirebaseAuthService firebaseAuthService =
Provider.of<FirebaseAuthService>(context, listen: false);
final RoleService roleService =
Provider.of<RoleService>(context, listen: false);
fireauth.User? user = firebaseAuthService.getUser();
if (user == null) {
Navigator.of(context).pushNamedAndRemoveUntil(
'/login-select', (Route<dynamic> route) => false);
} else {
String authToken = await firebaseAuthService.getUser()!.getIdToken();
await roleService.getAllRoles(authToken); // PAY ATTENTION HERE!
Navigator.of(context)
.pushNamedAndRemoveUntil('/home', (Route<dynamic> route) => false);
}
}
}
RoleService
class RoleService with ChangeNotifier {
NavLinks _navLinks = NavLinks();
late List<Role> _roles;
/// Return roles
List<Role> returnRoles() {
return _roles;
}
/// Get all Roles
Future<void> getAllRoles(String authToken) async {
try {
var data = await http.get(
Uri.parse(_navLinks.getAllRoles()),
headers: {HttpHeaders.authorizationHeader: "Bearer $authToken"},
);
var jsonData =
convert.json.decode(data.body).cast<Map<String, dynamic>>();
_roles = jsonData.map<Role>((json) => new Role.fromJson(json)).toList();
print(_roles);
} catch (error) {
print(error);
throw error;
}
}
}
All of my API calls are from the same URI. This "no caching" behavior is a problem for me because if I make more network calls in a row, it will take more time. For an example, if I make like 5 network calls it will take 7 seconds so on.
How can I cache the DNS Lookup, TCP Handshake and SSL Handshake in flutter?

Upload image to the server with the form data in flutter

i using the ImagePicker package in the dart when i pick the image i want to upload this to the server with the form data but when i try to send this i give this error
" Unhandled Exception: FileSystemException: Cannot retrieve length of file, path = 'File: '/storage/emulated/0/Android/data/com.example.aloteb/files/Pictures/scaled_image_picker3594752094355545880.jpg'' "
and this is my code for sending to the server
var request = http.MultipartRequest('POST', Uri.parse(url));
request.fields.addAll({
'data': '$map'
});
request.files.add(await http.MultipartFile.fromPath('image',picproviderformdate.getPAth.toString()));
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
print(await response.stream.bytesToString());
}
else {
print(response.reasonPhrase);
}
and this is my ImagePickercode
picprovider pic = Provider.of<picprovider>(context,listen: false);
File image = await ImagePicker.pickImage(
source: ImageSource.gallery, imageQuality: 50);
setState(() {
_image = image;
});
print(_image);
pic.setpathe(_image);
can any one help me for solve this problem?
I had a similar problem a while ago, and i used the Dio dependency instead of the classical Http link.
The code is very similar, and i can gave you an example.
final File file = File("${documentDirectory.path}/picture.png");
final httpDio = dio.Dio();
final formData = dio.FormData.fromMap({
"data": "{}",
"files.image": await dio.MultipartFile.fromFile(
"${documentDirectory.path}/picture.png",
filename: "picture.png",
contentType: MediaType('image', 'png'))
});
try {
final dio.Response response = await httpDio.post(
"ApiEndpoint/avatars",
data: formData,
options: dio.Options(headers: {"Authorization": "Bearer yourTokenIfNeeded"}));
if (response.statusCode == 200) {
// Success
}
} on dio.DioError catch (e) {
if (e.response != null) {
// Error
print(e.response.data);
return;
}
}
Don't forgot to update the API endpoint and route as well as your auth authorization if you need one.

React native debug logging stucks

I have a link in the back-end, so I fetch a post request to that link and receive a response. When I alert that response it gives a body init and body text in which I receive datas I need. Everything is good. But..
When I enable remote debugging and console.log that response, it gives body init and body blob (and both are empty). It stucks when I eneble debugging..
Thanks for attention ))
My code:
logIn = async (username, password) => {
// alert(`username : ${username}\n password : ${password}`);
let loginFormData = new FormData();
loginFormData.append('LoginForm[username]', username);
loginFormData.append('LoginForm[password]', password);
loginFormData.append('MacAddress', '111');
loginFormData.append('loginType', 'mobile');
try {
fetch('http://192.168.2.115/araqich_client/general/default/logout', {
method: 'POST',
body: loginFormData
});
let request = fetch('http://192.168.2.115/araqich_client/general/default/login', {
method: 'POST',
body: loginFormData
});
let loginResponseJson = await request;
if (loginResponseJson && loginResponseJson != null ) {
// let loginResponse = JSON.parse(loginResponseJson._bodyInit);
alert(JSON.stringify(loginResponseJson._bodyInit));
let status = loginResponse.status;
if (status) {
let SyncFormData = new FormData();
let accessToken = loginResponse.ACCESS_TOKEN;
SyncFormData.append('ACCESS_TOKEN', accessToken);
SyncFormData.append('MacAddress', '111');
SyncFormData.append('loginType', 'mobile');
let syncRequest = fetch('http://192.168.2.115/araqich_client/mobile/defaultA/syncAndroid', {
method: 'POST',
body: SyncFormData
});
let syncResponseJson = await syncRequest;
if (syncResponseJson && syncResponseJson != null) {
let syncResponse = JSON.parse(syncResponseJson._bodyInit);
let status = syncResponse.status;
if (!status) {
alert('Sorry(( something went wrong...');
} else {
alert('Life is good)))');
}
}
} else {
alert('else1')
}
} else {
alert('else')
}
} catch (error) {
alert(error);
}
}
Instead of using console.log statements, I'd advise using your debugger.

Categories

Resources