Skip to content

Instantly share code, notes, and snippets.

@TimurMukhortov
Last active May 16, 2024 19:18
Show Gist options
  • Save TimurMukhortov/a1c9819e3779015e54bc3964b7d2308a to your computer and use it in GitHub Desktop.
Save TimurMukhortov/a1c9819e3779015e54bc3964b7d2308a to your computer and use it in GitHub Desktop.
Dio authorization Interceptor with QueuedInterceptorsWrapper
class AuthorizationInterceptor extends QueuedInterceptorsWrapper {
final TokenRepository _tokenRepository;
final AuthorizationRepository _authorizationRepository;
final RefreshTokenRepository _refreshTokenRepository;
AuthorizationInterceptor({
required TokenRepository tokenRepository,
required AuthorizationRepository authorizationRepository,
required RefreshTokenRepository refreshTokenRepository,
}) : _tokenRepository = tokenRepository,
_authorizationRepository = authorizationRepository,
_refreshTokenRepository = refreshTokenRepository;
@override
void onRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) async {
final String? token = _tokenRepository.access;
if (token != null && token.isNotEmpty) {
options.headers.addAll(
<String, String>{
'Authorization': 'Bearer $token',
},
);
}
handler.next(options);
}
@override
void onError(DioError err, ErrorInterceptorHandler handler) async {
if (err.response?.statusCode == 401) {
final refreshToken = _tokenRepository.refresh;
if (refreshToken == null || refreshToken.isEmpty) {
_tokenRepository.clear();
_authorizationRepository.reauthorize();
return handler.next(err);
}
try {
final AuthenticationEntity authenticationEntity =
await _refreshTokenRepository.refresh(refreshToken);
await _tokenRepository.update(
access: authenticationEntity.accessToken,
refresh: authenticationEntity.refreshToken,
);
final RequestOptions requestOptions = err.response!.requestOptions;
requestOptions.headers['Authorization'] =
'Bearer ${authenticationEntity.accessToken}';
final options = Options(
method: requestOptions.method,
headers: requestOptions.headers,
);
final Dio dioRefresh = Dio(
BaseOptions(
baseUrl: requestOptions.baseUrl,
headers: <String, String>{
'accept': 'application/json',
},
),
);
final response = await dioRefresh.request<dynamic>(
requestOptions.path,
data: requestOptions.data,
queryParameters: requestOptions.queryParameters,
options: options,
);
return handler.resolve(response);
} on DioError {
if (err.response?.statusCode == 401) {
await _tokenRepository.clear();
await _authorizationRepository.reauthorize();
}
}
}
handler.next(err);
}
}
@TimurMukhortov
Copy link
Author

It run perfectly at first, but after first "refresh" success, the refreshToken function called as much as the number of failed request

This can happen if you make a request and the server returns a 401 error every time.

Check that after refreshing the token, you write the new one and the new token is valid

First, check all requests in some kind of rest client, such as postman or insomnia

If there are no errors, then it is already in the code how exactly you save the token and whether there is access to the storage where it is stored

@x-ji
Copy link

x-ji commented May 16, 2024

When I fire two GQL requests in the same component, both requests try to refresh the tokens. There doesn't seem to be logic here to let the second request understand that the first request already refreshed the token.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment