How can I refresh JWT tokens automatically in Flutter using Dio interceptors? #188720
-
BodyI'm building a Flutter application that uses Dio for HTTP requests and JWT tokens for authentication. When the access token expires, the server returns a 401 response. I want to automatically refresh the token using a refresh token and retry the original request. However, I'm concerned about multiple requests triggering the refresh process at the same time. What is the best way to implement a token refresh mechanism using Dio interceptors without creating multiple refresh calls? Guidelines
|
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
|
You can solve this problem by using a single refresh lock mechanism so that only one request refreshes the token while other requests wait. The idea is:
Example implementation using Dio interceptors: class TokenInterceptor extends Interceptor {
final Dio dio;
bool isRefreshing = false;
List<RequestOptions> pendingRequests = [];
TokenInterceptor(this.dio);
@override
void onError(DioException err, ErrorInterceptorHandler handler) async {
if (err.response?.statusCode == 401) {
if (!isRefreshing) {
isRefreshing = true;
try {
final refreshResponse = await dio.post("/refresh-token");
final newToken = refreshResponse.data["access_token"];
dio.options.headers["Authorization"] = "Bearer $newToken";
// retry pending requests
for (var request in pendingRequests) {
request.headers["Authorization"] = "Bearer $newToken";
dio.fetch(request);
}
pendingRequests.clear();
} catch (e) {
return handler.reject(err);
} finally {
isRefreshing = false;
}
}
pendingRequests.add(err.requestOptions);
return;
}
return handler.next(err);
}
}
If this helps please mark the answer as accepted ✔ |
Beta Was this translation helpful? Give feedback.
-
|
A robust way to solve this problem is to combine a refresh lock mechanism with request queuing to ensure that only one refresh request is executed at a time. The typical pattern works like this:
A simplified example structure could look like this: class AuthInterceptor extends Interceptor {
final Dio dio;
bool isRefreshing = false;
List<RequestOptions> pendingRequests = [];
AuthInterceptor(this.dio);
@override
void onError(DioException err, ErrorInterceptorHandler handler) async {
if (err.response?.statusCode == 401) {
if (isRefreshing) {
pendingRequests.add(err.requestOptions);
return;
}
isRefreshing = true;
try {
final refreshResponse = await dio.post("/auth/refresh");
final newToken = refreshResponse.data["access_token"];
dio.options.headers["Authorization"] = "Bearer $newToken";
for (final request in pendingRequests) {
request.headers["Authorization"] = "Bearer $newToken";
dio.fetch(request);
}
pendingRequests.clear();
} catch (e) {
return handler.reject(err);
} finally {
isRefreshing = false;
}
}
return handler.next(err);
}
}
Some additional best practices: • Store tokens in a secure storage solution. |
Beta Was this translation helpful? Give feedback.
A robust way to solve this problem is to combine a refresh lock mechanism with request queuing to ensure that only one refresh request is executed at a time.
The typical pattern works like this:
A simplified example structure could look like this: