Skip to content

Commit 8aa25ec

Browse files
Some logging improvements and better handling of certificate fetcher failing
1 parent f33b21c commit 8aa25ec

File tree

2 files changed

+57
-20
lines changed

2 files changed

+57
-20
lines changed

android/src/main/java/com/criticalblue/approov_service_flutter_httpclient/ApproovHttpClientPlugin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public void run() {
118118
// send any exception back to the Flutter Dart layer using the handler to
119119
// ensure it is done on the main thread
120120
resultMap.put("TransactionID", transactionID);
121-
resultMap.put("Exception", e.getLocalizedMessage());
121+
resultMap.put("Error", e.getLocalizedMessage());
122122
countDownLatch.countDown();
123123
if (handler != null)
124124
handler.post(() -> fgChannel.invokeMethod("response", resultMap));

lib/approov_service_flutter_httpclient.dart

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ class ApproovException implements Exception {
120120
ApproovException(String cause) {
121121
this.cause = cause;
122122
}
123+
124+
@override
125+
String toString() {
126+
return "ApproovException: $cause";
127+
}
123128
}
124129

125130
/// ApproovNetworkException indicates an exception caused by networking conditions which is likely to be
@@ -129,6 +134,11 @@ class ApproovNetworkException extends ApproovException {
129134
///
130135
/// @param cause is a message giving the cause of the exception
131136
ApproovNetworkException(String cause) : super(cause) {}
137+
138+
@override
139+
String toString() {
140+
return "ApproovNetworkException: $cause";
141+
}
132142
}
133143

134144
/// ApproovRejectionException provides additional information if the app has been rejected by Approov.
@@ -148,6 +158,11 @@ class ApproovRejectionException extends ApproovException {
148158
this.arc = arc;
149159
this.rejectionReasons = rejectionReasons;
150160
}
161+
162+
@override
163+
String toString() {
164+
return "ApproovRejectionException: $cause ARC:$arc reasons:$rejectionReasons";
165+
}
151166
}
152167

153168
// ApproovService is a singleton for managing the underlying Approov SDK itself. It provides a number of user accessible
@@ -1135,8 +1150,7 @@ class ApproovService {
11351150
/// require another probe.
11361151
///
11371152
/// @param url for where to retrieve the certificates with a GET request
1138-
/// @return a list of certificates (each as a Uint8list) for the host specified in the URL, null if an error occurred,
1139-
/// or an empty list if no suitable certificates are available.
1153+
/// @return a list of certificates (each as a Uint8list) for the host specified in the URL or null if an error occurred
11401154
static Future<List<Uint8List>?> _fetchHostCertificates(Uri url) async {
11411155
List<Uint8List>? hostCertificates = _hostCertificates[url.host];
11421156
if (hostCertificates == null) {
@@ -1177,17 +1191,32 @@ class ApproovService {
11771191
_platformTransactions.remove(transactionID);
11781192
}
11791193

1180-
// wait on the transaction to complete
1194+
// wait on the transaction to complete and process the results
11811195
final results = await completer.future;
11821196
if (results is Map<Object?, Object?>) {
1183-
List fetchedHostCertificates = results["Certificates"] as List<dynamic>;
1184-
hostCertificates = [];
1185-
for (final cert in fetchedHostCertificates) {
1186-
hostCertificates.add(cert as Uint8List);
1197+
if (results.containsKey("Certificates")) {
1198+
// certificate were fetched so we cache them
1199+
List fetchedHostCertificates = results["Certificates"] as List<dynamic>;
1200+
hostCertificates = [];
1201+
for (final cert in fetchedHostCertificates) {
1202+
hostCertificates.add(cert as Uint8List);
1203+
}
1204+
_hostCertificates[url.host] = hostCertificates;
1205+
Log.d("$TAG: $isolate fetchHostCertificates ${url.host} obtained ${hostCertificates.length} certificates");
1206+
} else if (results.containsKey("Error")) {
1207+
// there was a specific error fetching the certificates
1208+
String error = results["Error"] as String;
1209+
Log.d("$TAG: $isolate fetchHostCertificates ${url.host}: $error");
1210+
return null;
1211+
} else {
1212+
// there was an unknown error fetching the certificates
1213+
Log.d("$TAG: $isolate fetchHostCertificates ${url.host} error");
1214+
return null;
11871215
}
1188-
1189-
// cache the obtained host certificates
1190-
_hostCertificates[url.host] = hostCertificates;
1216+
} else {
1217+
// there was an unknown return format fetching the certificates
1218+
Log.d("$TAG: $isolate fetchHostCertificates ${url.host} bad response");
1219+
return null;
11911220
}
11921221
} catch (err) {
11931222
throw ApproovException('$err');
@@ -1557,7 +1586,7 @@ class _ApproovHttpClientRequest implements HttpClientRequest {
15571586
/// Dart IO library's HttpClient.
15581587
class ApproovHttpClient implements HttpClient {
15591588
// logging tag
1560-
static const String TAG = "ApproovHttpClient";
1589+
static const String TAG = "ApproovService:ApproovHttpClient";
15611590

15621591
// internal HttpClient delegate, will be rebuilt if pinning fails (or pins change). It is not set to a pinned
15631592
// HttpClient initially, but this is just used to hold any state updates that might occur before a connection
@@ -1572,7 +1601,7 @@ class ApproovHttpClient implements HttpClient {
15721601
// re-create the delegate pinned HttpClient.
15731602
String? _connectedHost;
15741603

1575-
// indicates whether the ApproovHttpClient has been closed by calling close().
1604+
// indicates whether the ApproovHttpClient has been closed by calling close()
15761605
bool _isClosed = false;
15771606

15781607
// state required to implement getters and setters required by the HttpClient interface
@@ -1594,17 +1623,20 @@ class ApproovHttpClient implements HttpClient {
15941623
/// @param cert is the certificate which could not be authenticated
15951624
/// @param host is the host name of the server to which the request is being sent
15961625
/// @param port is the port of the server
1626+
/// @return false to prevent the request from being sent
15971627
bool _pinningFailureCallback(X509Certificate cert, String host, int port) {
1628+
// reset host certificates and delegate pinned HttpClient connected host to force them to be recreated
1629+
Log.d("$TAG: pinning failure callback for $host");
1630+
ApproovService._removeCertificates(host);
1631+
_connectedHost = null;
1632+
1633+
// call any user defined function for its side effects only (as we are going to reject anyway)
15981634
Function(X509Certificate cert, String host, int port)? badCertificateCallback = _badCertificateCallback;
15991635
if (badCertificateCallback != null) {
1600-
// call the user defined function for its side effects only (as we are going to reject anyway)
16011636
badCertificateCallback(cert, host, port);
16021637
}
16031638

1604-
// reset host certificates and delegate pinned HttpClient connected host to force them to be recreated
1605-
Log.d("$TAG: Pinning failure callback for $host");
1606-
ApproovService._removeCertificates(host);
1607-
_connectedHost = null;
1639+
// prevent the request from being sent
16081640
return false;
16091641
}
16101642

@@ -1637,7 +1669,7 @@ class ApproovHttpClient implements HttpClient {
16371669
Map<dynamic, dynamic> allPins = {};
16381670
String isolate = ApproovService._isRootIsolate ? "root" : "background";
16391671
if (isExcluded) {
1640-
// get whatever pins we currrently have without forcing a fetch of new ones
1672+
// get whatever pins we currently have without forcing a fetch of new ones
16411673
allPins = await ApproovService._getPins("public-key-sha256");
16421674
} else {
16431675
// start the process of fetching an Approov token to get the latest configuration
@@ -1655,6 +1687,7 @@ class ApproovHttpClient implements HttpClient {
16551687
if (fetchResult.isConfigChanged) {
16561688
await ApproovService._fetchConfig();
16571689
ApproovService._removeAllCertificates();
1690+
Log.d("$TAG: $isolate configuration change");
16581691
}
16591692

16601693
// get pins from Approov - note that it is still possible at this point if the token fetch failed that no pins
@@ -1771,6 +1804,8 @@ class ApproovHttpClient implements HttpClient {
17711804
// if we have an active connection to a different host we need to tear down the delegate
17721805
// pinned HttpClient and create a new one with the correct pinning
17731806
if (_connectedHost != host) {
1807+
String isolate = ApproovService._isRootIsolate ? "root" : "background";
1808+
Log.d("$TAG: $isolate open pinned delegate creation for $host:$port");
17741809
Uri url = Uri(scheme: "https", host: host, port: port, path: path);
17751810
Future<HttpClient> futureDelegatePinnedHttpClient = _createPinnedHttpClient(url);
17761811
_futureDelegatePinnedHttpClient = futureDelegatePinnedHttpClient;
@@ -1802,6 +1837,8 @@ class ApproovHttpClient implements HttpClient {
18021837
// if we have an active connection to a different host we need to tear down the delegate
18031838
// pinned HttpClient and create a new one with the correct pinning
18041839
if (_connectedHost != url.host) {
1840+
String isolate = ApproovService._isRootIsolate ? "root" : "background";
1841+
Log.d("$TAG: $isolate openUrl pinned delegate creation for $url.host:$url.port");
18051842
Future<HttpClient> futureDelegatePinnedHttpClient = _createPinnedHttpClient(url);
18061843
_futureDelegatePinnedHttpClient = futureDelegatePinnedHttpClient;
18071844
HttpClient httpClient = await futureDelegatePinnedHttpClient;
@@ -1937,7 +1974,7 @@ class ApproovHttpClient implements HttpClient {
19371974
// and adds the desired behavior.
19381975
class ApproovClient extends http.BaseClient {
19391976
// logging tag
1940-
static const String TAG = "ApproovClient";
1977+
static const String TAG = "ApproovService:ApproovClient";
19411978

19421979
// internal client delegate used to perform the actual requests
19431980
http.Client? _delegateClient;

0 commit comments

Comments
 (0)