Skip to content

[webview_flutter] Adds support to respond to recoverable SSL certificate errors #9150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 55 commits into from
May 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
84edf6f
add interface portion
bparrishMines Apr 22, 2025
a986f6e
Merge branch 'main' of github.com:flutter/packages into webview_ssl
bparrishMines Apr 22, 2025
7735439
update dependencies
bparrishMines Apr 22, 2025
2a8cf7c
android impl without tests
bparrishMines Apr 23, 2025
f3a1122
fix test names
bparrishMines Apr 23, 2025
887104a
new pigeon generate
bparrishMines Apr 23, 2025
c2aa4a4
add certificate impl
bparrishMines Apr 23, 2025
f952665
update pigeon
bparrishMines Apr 23, 2025
095e9dc
implement auth request
bparrishMines Apr 23, 2025
1fbe36a
improve code a bit
bparrishMines Apr 23, 2025
945763d
start of front facing but need to restart
bparrishMines Apr 24, 2025
16c5e25
update platform interface
bparrishMines Apr 24, 2025
92c6078
android impl
bparrishMines Apr 24, 2025
7c145c4
ios impl
bparrishMines Apr 24, 2025
d3e43a4
webview_flutter impl
bparrishMines Apr 24, 2025
70f77e3
platform intrface error
bparrishMines Apr 24, 2025
0a55007
android docs
bparrishMines Apr 24, 2025
c195d52
navigation delegate tests with new mocks
bparrishMines Apr 24, 2025
ea1c23f
fix test mocks i guess
bparrishMines Apr 24, 2025
e46a9bf
finish android tests
bparrishMines Apr 24, 2025
a221ece
update proxy
bparrishMines Apr 24, 2025
6a7a4db
add webkit unit test
bparrishMines Apr 25, 2025
efd2ad9
add internal and error
bparrishMines Apr 25, 2025
2c6eb01
formatting
bparrishMines Apr 25, 2025
32b690a
Merge branch 'main' of github.com:flutter/packages into webview_ssl
bparrishMines Apr 25, 2025
6f24861
app facing tests
bparrishMines Apr 25, 2025
d5e3879
lint and docs
bparrishMines Apr 25, 2025
1f3ec87
add ssl handler to android
bparrishMines Apr 25, 2025
1d72722
update ios dart implementation
bparrishMines Apr 25, 2025
fa0aa28
update dart unit tests
bparrishMines Apr 25, 2025
c0ea481
fix swift side
bparrishMines Apr 25, 2025
28e7bb7
formatting
bparrishMines Apr 25, 2025
2981d00
fix ios tests
bparrishMines Apr 25, 2025
1f0aa84
formatting and any remove
bparrishMines Apr 25, 2025
f4d1e59
remove any again
bparrishMines Apr 25, 2025
364b3c1
add test to verify perform is default
bparrishMines Apr 25, 2025
54617f8
wording
bparrishMines May 13, 2025
a4ca56f
Merge branch 'main' of github.com:flutter/packages into webview_ssl
bparrishMines May 13, 2025
69686e9
move some logic to separate methods
bparrishMines May 13, 2025
1856f10
make it static
bparrishMines May 13, 2025
286c0d0
make proxy guaranteed
bparrishMines May 13, 2025
7a87558
Merge branch 'main' of github.com:flutter/packages into webview_ssl
bparrishMines May 17, 2025
ffb10b9
version bump
bparrishMines May 17, 2025
2f6240f
version bump and repigeoning
bparrishMines May 17, 2025
dff5ad8
fix android native tests
bparrishMines May 17, 2025
31547cb
fix cookiemanager test
bparrishMines May 17, 2025
e0d723d
fix test that should be skipped
bparrishMines May 19, 2025
e286e7f
use leak tracker
bparrishMines May 19, 2025
e2c2a1c
fix legacy test
bparrishMines May 19, 2025
a642178
Merge branch 'main' of github.com:flutter/packages into webview_ssl
bparrishMines May 19, 2025
e3c0723
fix pubspecs
bparrishMines May 19, 2025
0786506
version bump
bparrishMines May 19, 2025
6212d60
export certificate
bparrishMines May 19, 2025
81d57f7
test proceed is called
bparrishMines May 19, 2025
5c4db8c
formatting again
bparrishMines May 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/webview_flutter/webview_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 4.13.0

* Adds support to respond to recoverable SSL certificate errors. See `NavigationDelegate(onSSlAuthError)`.

## 4.12.0

* Adds support to set whether to draw the scrollbar. See
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
webview_flutter_android: ^4.5.0
webview_flutter_wkwebview: ^3.21.0
webview_flutter_android: ^4.7.0
webview_flutter_wkwebview: ^3.22.0

dev_dependencies:
build_runner: ^2.1.5
Expand All @@ -27,7 +27,7 @@ dev_dependencies:
sdk: flutter
integration_test:
sdk: flutter
webview_flutter_platform_interface: ^2.12.0
webview_flutter_platform_interface: ^2.13.0

flutter:
uses-material-design: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ class NavigationDelegate {
/// Constructs a [NavigationDelegate].
///
/// {@template webview_fluttter.NavigationDelegate.constructor}
/// `onUrlChange`: invoked when the underlying web view changes to a new url.
/// `onHttpAuthRequest`: invoked when the web view is requesting authentication.
/// **`onUrlChange`:** invoked when the underlying web view changes to a new url.
/// **`onHttpAuthRequest`:** invoked when the web view is requesting authentication.
/// **`onSslAuthError`:** Invoked when the web view receives a recoverable SSL
/// error for a certificate. The host application must call either
/// [SslAuthError.cancel] or [SslAuthError.proceed].
/// {@endtemplate}
NavigationDelegate({
FutureOr<NavigationDecision> Function(NavigationRequest request)?
Expand All @@ -51,6 +54,7 @@ class NavigationDelegate {
void Function(UrlChange change)? onUrlChange,
void Function(HttpAuthRequest request)? onHttpAuthRequest,
void Function(HttpResponseError error)? onHttpError,
void Function(SslAuthError request)? onSslAuthError,
}) : this.fromPlatformCreationParams(
const PlatformNavigationDelegateCreationParams(),
onNavigationRequest: onNavigationRequest,
Expand All @@ -61,6 +65,7 @@ class NavigationDelegate {
onUrlChange: onUrlChange,
onHttpAuthRequest: onHttpAuthRequest,
onHttpError: onHttpError,
onSslAuthError: onSslAuthError,
);

/// Constructs a [NavigationDelegate] from creation params for a specific
Expand Down Expand Up @@ -105,6 +110,7 @@ class NavigationDelegate {
void Function(UrlChange change)? onUrlChange,
void Function(HttpAuthRequest request)? onHttpAuthRequest,
void Function(HttpResponseError error)? onHttpError,
void Function(SslAuthError request)? onSslAuthError,
}) : this.fromPlatform(
PlatformNavigationDelegate(params),
onNavigationRequest: onNavigationRequest,
Expand All @@ -115,6 +121,7 @@ class NavigationDelegate {
onUrlChange: onUrlChange,
onHttpAuthRequest: onHttpAuthRequest,
onHttpError: onHttpError,
onSslAuthError: onSslAuthError,
);

/// Constructs a [NavigationDelegate] from a specific platform implementation.
Expand All @@ -130,6 +137,7 @@ class NavigationDelegate {
void Function(UrlChange change)? onUrlChange,
HttpAuthRequestCallback? onHttpAuthRequest,
void Function(HttpResponseError error)? onHttpError,
void Function(SslAuthError request)? onSslAuthError,
}) {
if (onNavigationRequest != null) {
platform.setOnNavigationRequest(onNavigationRequest!);
Expand All @@ -155,6 +163,13 @@ class NavigationDelegate {
if (onHttpError != null) {
platform.setOnHttpError(onHttpError);
}
if (onSslAuthError != null) {
platform.setOnSSlAuthError(
(PlatformSslAuthError error) {
onSslAuthError(SslAuthError._fromPlatform(error));
},
);
}
}

/// Implementation of [PlatformNavigationDelegate] for the current platform.
Expand Down Expand Up @@ -184,3 +199,54 @@ class NavigationDelegate {
/// Invoked when a resource loading error occurred.
final WebResourceErrorCallback? onWebResourceError;
}

/// Represents an SSL error with the associated certificate.
///
/// The host application must call [cancel] or, contrary to secure web
/// communication standards, [proceed] to provide the web view's response to the
/// error. [proceed] should generally only be used in test environments, as
/// using it in production can expose users to security and privacy risks.
///
/// ## Platform-Specific Features
/// This class contains an underlying implementation provided by the current
/// platform. Once a platform implementation is imported, the examples below
/// can be followed to use features provided by a platform's implementation.
///
/// Below is an example of accessing the platform-specific implementation for
/// iOS and Android:
///
/// ```dart
/// final SslAuthError error = ...;
///
/// if (WebViewPlatform.instance is WebKitWebViewPlatform) {
/// final WebKitSslAuthError webKitError =
/// error.platform as WebKitSslAuthError;
/// } else if (WebViewPlatform.instance is AndroidWebViewPlatform) {
/// final AndroidSslAuthError androidError =
/// error.platform as AndroidSslAuthError;
/// }
/// ```
class SslAuthError {
SslAuthError._fromPlatform(this.platform);

/// An implementation of [PlatformSslAuthError] for the current platform.
final PlatformSslAuthError platform;

/// The certificate associated with this error.
X509Certificate? get certificate => platform.certificate;

/// Instructs the WebView that encountered the SSL certificate error to
/// terminate communication with the server.
///
/// The host application must call this method to prevent a resource from
/// loading when an SSL certificate is invalid.
Future<void> cancel() => platform.cancel();

/// Instructs the WebView that encountered the SSL certificate error to ignore
/// the error and continue communicating with the server.
///
/// **Warning:** Calling [proceed] in a production environment is strongly
/// discouraged, as an invalid SSL certificate means that the connection is
/// not secure, so proceeding can expose users to security and privacy risks.
Future<void> proceed() => platform.proceed();
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export 'package:webview_flutter_platform_interface/webview_flutter_platform_inte
WebViewCredential,
WebViewOverScrollMode,
WebViewPermissionResourceType,
WebViewPlatform;
WebViewPlatform,
X509Certificate;

export 'src/navigation_delegate.dart';
export 'src/webview_controller.dart';
Expand Down
8 changes: 4 additions & 4 deletions packages/webview_flutter/webview_flutter/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: webview_flutter
description: A Flutter plugin that provides a WebView widget backed by the system webview.
repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
version: 4.12.0
version: 4.13.0

environment:
sdk: ^3.6.0
Expand All @@ -21,9 +21,9 @@ flutter:
dependencies:
flutter:
sdk: flutter
webview_flutter_android: ^4.5.0
webview_flutter_platform_interface: ^2.12.0
webview_flutter_wkwebview: ^3.21.0
webview_flutter_android: ^4.7.0
webview_flutter_platform_interface: ^2.13.0
webview_flutter_wkwebview: ^3.22.0

dev_dependencies:
build_runner: ^2.1.5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import 'package:webview_flutter_platform_interface/webview_flutter_platform_inte

import 'navigation_delegate_test.mocks.dart';

@GenerateMocks(<Type>[WebViewPlatform, PlatformNavigationDelegate])
@GenerateMocks(<Type>[
WebViewPlatform,
PlatformNavigationDelegate,
PlatformSslAuthError,
])
void main() {
group('NavigationDelegate', () {
test('onNavigationRequest', () async {
Expand Down Expand Up @@ -111,6 +115,28 @@ void main() {

verify(delegate.platform.setOnHttpError(onHttpError));
});

test('onSslAuthError', () async {
WebViewPlatform.instance = TestWebViewPlatform();

final NavigationDelegate delegate = NavigationDelegate(
onSslAuthError: expectAsync1((SslAuthError error) {
error.proceed();
}),
);

final void Function(PlatformSslAuthError) callback = verify(
(delegate.platform as MockPlatformNavigationDelegate)
.setOnSSlAuthError(captureAny))
.captured
.single as void Function(PlatformSslAuthError);

final MockPlatformSslAuthError mockPlatformError =
MockPlatformSslAuthError();
callback(mockPlatformError);

verify(mockPlatformError.proceed());
});
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
import 'dart:async' as _i8;

import 'package:mockito/mockito.dart' as _i1;
import 'package:mockito/src/dummies.dart' as _i10;
import 'package:webview_flutter_platform_interface/src/platform_navigation_delegate.dart'
as _i3;
import 'package:webview_flutter_platform_interface/src/platform_ssl_auth_error.dart'
as _i9;
import 'package:webview_flutter_platform_interface/src/platform_webview_controller.dart'
as _i4;
import 'package:webview_flutter_platform_interface/src/platform_webview_cookie_manager.dart'
Expand Down Expand Up @@ -211,4 +214,47 @@ class MockPlatformNavigationDelegate extends _i1.Mock
returnValue: _i8.Future<void>.value(),
returnValueForMissingStub: _i8.Future<void>.value(),
) as _i8.Future<void>);

@override
_i8.Future<void> setOnSSlAuthError(
_i3.SslAuthErrorCallback? onSslAuthError,
) =>
(super.noSuchMethod(
Invocation.method(#setOnSSlAuthError, [onSslAuthError]),
returnValue: _i8.Future<void>.value(),
returnValueForMissingStub: _i8.Future<void>.value(),
) as _i8.Future<void>);
}

/// A class which mocks [PlatformSslAuthError].
///
/// See the documentation for Mockito's code generation for more information.
class MockPlatformSslAuthError extends _i1.Mock
implements _i9.PlatformSslAuthError {
MockPlatformSslAuthError() {
_i1.throwOnMissingStub(this);
}

@override
String get description => (super.noSuchMethod(
Invocation.getter(#description),
returnValue: _i10.dummyValue<String>(
this,
Invocation.getter(#description),
),
) as String);

@override
_i8.Future<void> proceed() => (super.noSuchMethod(
Invocation.method(#proceed, []),
returnValue: _i8.Future<void>.value(),
returnValueForMissingStub: _i8.Future<void>.value(),
) as _i8.Future<void>);

@override
_i8.Future<void> cancel() => (super.noSuchMethod(
Invocation.method(#cancel, []),
returnValue: _i8.Future<void>.value(),
returnValueForMissingStub: _i8.Future<void>.value(),
) as _i8.Future<void>);
}
Original file line number Diff line number Diff line change
Expand Up @@ -458,4 +458,14 @@ class MockPlatformNavigationDelegate extends _i1.Mock
returnValue: _i5.Future<void>.value(),
returnValueForMissingStub: _i5.Future<void>.value(),
) as _i5.Future<void>);

@override
_i5.Future<void> setOnSSlAuthError(
_i6.SslAuthErrorCallback? onSslAuthError,
) =>
(super.noSuchMethod(
Invocation.method(#setOnSSlAuthError, [onSslAuthError]),
returnValue: _i5.Future<void>.value(),
returnValueForMissingStub: _i5.Future<void>.value(),
) as _i5.Future<void>);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ void main() {
main_file.PlatformWebViewPermissionRequest;
main_file.PlatformWebViewWidgetCreationParams;
main_file.ProgressCallback;
main_file.UrlChange;
main_file.WebViewOverScrollMode;
main_file.WebViewPermissionResourceType;
main_file.WebResourceError;
Expand All @@ -39,7 +40,7 @@ void main() {
main_file.WebResourceErrorType;
main_file.WebResourceRequest;
main_file.WebResourceResponse;
main_file.UrlChange;
main_file.X509Certificate;
},
);
});
Expand Down