How to use http interceptor in a flutter project? - flutter

I have to add header to all my Api's. I was told to use http interceptor for that. But i am not able to understand how to do it as i am new to flutter. Can anyone help me with example?

you can use http_interceptor.
it works as follows,
first you create your interceptor by implementing InterceptorContract
class MyInterceptor implements InterceptorContract {
#override
Future<RequestData> interceptRequest({RequestData data}) async {
try {
data.headers["Content-Type"] = "application/json";
} catch (e) {
print(e);
}
return data;
}
#override
Future<ResponseData> interceptResponse({ResponseData data}) async => data;
}
then create a client and inject this interceptor in it
Client _client = InterceptedClient.build(interceptors: [
MyInterceptor(),
]);
You can add multiple interceptors to the same client, say you want one to refresh the token, one to add/change headers, so it will be something like this:
Client _client = InterceptedClient.build(interceptors: [
RefreshTokenInterceptor(),
ContentTypeInterceptor(),
/// etc
]);
note that every interceptor must implement the InterceptorContract
Now anytime you use this client, the request will be intercepted and headers will be added to it. You can make this client a singleton to use the same instance across the app like this
class HttpClient {
Client _client;
static void _initClient() {
if (_client == null) {
_client = InterceptedClient.build(
interceptors: [MyInterceptor()],
);
}
}
/// implement http request with this client
}

Related

how to redirect user to logout page when token or refresh token expired using bloc in flutter

This is my base repository where it gets the data from the bloc I want to redirect the user to a custom page like logout when the token expires or refresh token expire. I change pages, in bloc consumer or bloc listener but in this case, this is stupid work because I should replace bloc consumer with another bloc method everywhere I request to the server
#override
Future<Map<String, dynamic>> get(String url) async {
// Uri newUrl = Uri.parse(baseUrl + url);
var newUrl = baseUrl + url;
dio.Response response;
try {
// x.interceptors.add(dio.InterceptorsWrapper(
// onError: (e, handler) {
// if(e.response!.statusCode == 401)
// redirect user to custom page
// },
// ));
response = await dio.Dio()
.get(newUrl, options: dio.Options(headers: headersList))
.timeout(duration);
var result = _processResponse(response);
if (result.runtimeType.toString().toLowerCase() == 'string') {
return <String, String>{'key': result};
}
return result;
} on SocketException {
throw FetchDataException('internetConnection');
} on TimeoutException {
throw ApiNotRespondingException('api not found ');
} on Exception catch (e) {
var response = e as dio.DioError;
print(e.toString());
throw _handleError(response);
}
}
I recently asked myself the same question and come up with this solution (maybe not the best one).
You need a separated bloc for containing jwt tokens;
Init this bloc as a singleton. I use injectable that uses get_it as a DI
#singleton
class TokenBloc extends Bloc<TokenEvent, TokenState> {
final ISecureStorage secureStorage;
TokenBloc(this.secureStorage) : super(TokenInitial()) {
on<TokenReseted>(delete);
on<TokenUpdated>(put);
}
put(event, emit) {
emit(TokenAuthentificated(event.accessToken, event.refreshToken));
secureStorage.write('accessToken', event.accessToken);
secureStorage.write('refreshToken', event.refreshToken);
}
delete(event, emit) {
emit(TokenDeleted());
emit(TokenInitial());
secureStorage.write('accessToken', null);
secureStorage.write('refreshToken', null);
}
}
Wrap your Home widget with BlocProvider like that
return BlocProvider.value(
value: getIt.get<TokenBloc>(),
child: const CupertinoApp(
home: Home(),
),
);
Create a single module for requesting a new refresh token from API, which will use this TokenBloc for updating/deleting tokens. You can access to TokenBloc from RefreshTokenAPI using getIt.
getIt.get<TokenBloc>().add(TokenReseted());
All you have to do is to listen to the TokenBloc changes in your UI and make a redirect.
That’s it. Hope it will help :)

How to intercept GRPC response

I am using flutter for my mobile application. I can intercept requests. But I don't know how to intercept response in GRPC in flutter.
How can I intercept GRPC response in flutter?
It looks it is pretty easy.
First:
class LoggerInterceptor extends ClientInterceptor {
static final LoggerInterceptor _instance = LoggerInterceptor._();
static LoggerInterceptor get instance => _instance;
LoggerInterceptor._();
#override
ResponseFuture<R> interceptUnary<Q, R>(
ClientMethod<Q, R> method, Q request, CallOptions options, ClientUnaryInvoker<Q, R> invoker) {
logger.d(
'Grpc request. '
'method: ${method.path}, '
'request: $request',
);
final response = super.interceptUnary(
method,
request,
options,
invoker,
);
response.then((r) {
logger.d(
'Grpc response. '
'method: ${method.path}, '
'response: ${Utils.getSubstring(r.toString(), 3000)}',
);
});
return response;
}
}
Second: While creating client add interceptor to the interceptors:
myClient = SomeServiceClient(
clientChannel,
interceptors: [
InfoInterceptor.instance,
LoggerInterceptor.instance,
],
),

How to pin public key of SSL certificate in flutter?

Could someone help me on implementing SSL public key pinning in flutter? I have searched a lot in google but I did not find a proper article that explains how this can be implemented.
Kindly help !!!
There is a package called http_certificate_pinning which provides 3 different APIs to use. You can check it here.
1-As an interceptor for Dio:
import 'package:http_certificate_pinning/certificate_pinning_interceptor.dart';
// Add CertificatePinningInterceptor in dio Client
Dio getClient(String baseUrl, List<String> allowedSHAFingerprints){
var dio = Dio(BaseOptions(baseUrl: baseUrl))
..interceptors.add(CertificatePinningInterceptor(allowedSHAFingerprints));
return dio;
}
myRepositoryMethod(){
dio.get("myurl.com");
}
2-Creating an http client:
import 'package:http_certificate_pinning/secure_http_client.dart';
// Uses SecureHttpClient to make requests
SecureHttpClient getClient(List<String> allowedSHAFingerprints){
final secureClient = SecureHttpClient.build(certificateSHA256Fingerprints);
return secureClient;
}
myRepositoryMethod(){
secureClient.get("myurl.com");
}
3-Checking if the handshake happens correctly and do whatever you want:
import 'package:http_certificate_pinning/http_certificate_pinning.dart';
Future myCustomImplementation(String url, Map<String,String> headers, List<String> allowedSHAFingerprints) async {
try{
final secure = await HttpCertificatePinning.check(
serverURL: url,
headerHttp: headers,
sha: SHA.SHA256,
allowedSHAFingerprints:allowedSHAFingerprints,
timeout : 50
);
if(secure.contains("CONNECTION_SECURE")){
return true;
}else{
return false;
}
}catch(e){
return false;
}
}

How to close stream on app shutdown when created in main?

I have a custom http client that intercepts responses to provide service unavailable events via a stream:
class MyHttpClient extends http.BaseClient {
final String apiBase;
MyHttpClient({required this.apiBase});
final _controller = StreamController<ServiceUnavailableEvent>.broadcast();
Stream<ServiceUnavailableEvent> get stream => _controller.stream;
#override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
final response = await request.send();
if (request.url.toString().startsWith(apiBase) &&
response.statusCode == HttpStatus.serviceUnavailable) {
_controller.sink.add(ServiceUnavailableEvent(isAvailable: false));
} else {
_controller.sink.add(ServiceUnavailableEvent(isAvailable: true));
}
return response;
}
Future<void> close() async {
await _controller.close();
}
}
The client is instanciated once in the main flutter function:
void main() {
final client = MyHttpClient(apiBase: apiBase)
runApp(MyApp(client: client));
}
Everything works fine and I added the close method on the custom http client that closes the stream. But when/how can I call this close method ?
I thought about AppLifeCycleEvent but all state seem to be the wrong place, because I only want to close the client when the app really shuts down (i.e. if the user re-opens/resumes the app a new client must be created in order to recreate the subscription).
how do you know it is still open when app shutdown. i think platform(ios/android) close it.

Flutter Global Http Interceptor

I would like to know if it is possible to have a global HTTP interceptor to attach token in header for all requests in Flutter? I've searched a lot and couldn't find any information as where and how to set it up as globally. Thanks a lot!
You can extend BaseClient and override send(BaseRequest request):
class CustomClient extends BaseClient {
static Map<String, String> _getHeaders() {
return {
'Authentication': 'c7fabcDefG04075ec6ce0',
};
}
#override
Future<StreamedResponse> send(BaseRequest request) async {
request.headers.addAll(_getHeaders());
return request.send();
}
}
In the above example the 'Authentication': 'c7fabcDefG04075ec6ce0' is hardcoded and not encrypted which you should never do.
Using dio package u can do that :
Dio dio = Dio(BaseOptions(
connectTimeout: 30000,
baseUrl: 'your api',
responseType: ResponseType.json,
contentType: ContentType.json.toString(),
))
..interceptors.addAll(
[
InterceptorsWrapper(onRequest: (RequestOptions requestOptions) {
dio.interceptors.requestLock.lock();
String token = ShareP.sharedPreferences.getString('token');
if (token != null) {
dio.options.headers[HttpHeaders.authorizationHeader] =
'Bearer ' + token;
}
dio.interceptors.requestLock.unlock();
return requestOptions;
}),
// other interceptor
],
);
Flutter provides http_interceptor.dart package.
Sample
class LoggingInterceptor implements InterceptorContract {
#override
Future<RequestData> interceptRequest({RequestData data}) async {
print(data);
return data;
}
#override
Future<ResponseData> interceptResponse({ResponseData data}) async {
print(data);
return data;
}
}
This answer is an extension of Felipe Medeiros's answer that I could not edit. It is not actually a global way to attach a token to every requests, but should be considered nonetheless to create interceptors/middleware.
BaseClient is part of the native http package. You can extend BaseClient and override send(BaseRequest request):
class BearerTokenMiddleware extends BaseClient {
final Future<String> Function() getBearerToken;
BearerTokenMiddleware({required this.getBearerToken});
#override
Future<StreamedResponse> send(BaseRequest request) async {
request.headers.addAll({
'Authorization': 'Bearer ${await getBearerToken()}',
});
return request.send();
}
}
When one of your classes needs the http client, inject the BaseClient abstraction to the constructor. Exemple:
class HTTPTodoGateway implements TodoGateway {
final BaseClient httpClient;
HTTPTodoGateway ({required this.httpClient});
getTodoById(string todoId) {
httpClient.get(Uri.parse('https://mytodos/$todoId'));
}
}
You can then create a new instance of HTTPTodoGateway with an instance of BearerTokenMiddleware that will wrap your requests with an authentication bearer header.