I am trying to use Interceptor with Dio in flutter, I have to handle Token expire. following is my code
Future<Dio> getApiClient() async { token = await storage.read(key: USER_TOKEN); _dio.interceptors.clear(); _dio.interceptors .add(InterceptorsWrapper(onRequest: (RequestOptions options) { // Do something before request is sent options.headers["Authorization"] = "Bearer " + token; return options; },onResponse:(Response response) { // Do something with response data return response; // continue }, onError: (DioError error) async { // Do something with response error if (error.response?.statusCode == 403) { // update token and repeat // Lock to block the incoming request until the token updated _dio.interceptors.requestLock.lock(); _dio.interceptors.responseLock.lock(); RequestOptions options = error.response.request; FirebaseUser user = await FirebaseAuth.instance.currentUser(); token = await user.getIdToken(refresh: true); await writeAuthKey(token); options.headers["Authorization"] = "Bearer " + token; _dio.interceptors.requestLock.unlock(); _dio.interceptors.responseLock.unlock(); _dio.request(options.path, options: options); } else { return error; } })); _dio.options.baseUrl = baseUrl; return _dio; }
problem is instead of repeating the network call with the new token, Dio is returning the error object to the calling method, which in turn is rendering the wrong widget, any leads on how to handle token refresh with dio?
In order to store our refresh token securely in the app, install Flutter Secure Storage by running in the terminal: flutter pub add flutter_secure_storage . Inside the constructor of Api is where we can add the access token to every request using interceptors with dio.
When you make a request with an expired access token, the status code 401 (Unauthorized) will appear in the response. In this case, you must request a new token from the server and make the previous request again with a valid access token.
In the URL field enter the address to the refresh token route of your local API - http://localhost:4000/users/refresh-token . Click the Send button, you should receive a "200 OK" response containing the user details and a JWT token, and a cookie containing a new refresh token.
I have found a simple solution that looks like the following:
this.api = Dio(); this.api.interceptors.add(InterceptorsWrapper( onError: (error) async { if (error.response?.statusCode == 403 || error.response?.statusCode == 401) { await refreshToken(); return _retry(error.request); } return error.response; }));
Basically what is going on is it checks to see if the error is a 401
or 403
, which are common auth errors, and if so, it will refresh the token and retry the response. My implementation of refreshToken()
looks like the following, but this may vary based on your api:
Future<void> refreshToken() async { final refreshToken = await this._storage.read(key: 'refreshToken'); final response = await this.api.post('/users/refresh', data: {'token': refreshToken}); if (response.statusCode == 200) { this.accessToken = response.data['accessToken']; } }
I use Flutter Sercure Storage to store the accessToken. My retry method looks like the following:
Future<Response<dynamic>> _retry(RequestOptions requestOptions) async { final options = new Options( method: requestOptions.method, headers: requestOptions.headers, ); return this.api.request<dynamic>(requestOptions.path, data: requestOptions.data, queryParameters: requestOptions.queryParameters, options: options); }
If you want to easily allows add the access_token
to the request I suggest adding the following function when you declare your dio router with the onError
callback:
onRequest: (options) async { options.headers['Authorization'] = 'Bearer: $accessToken'; return options; },
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With