Skip to content

Commit 2c54697

Browse files
authored
[webview_flutter] Support for handling basic authentication requests (Android) (flutter#5454)
Adds the Android implementation for basic http authentication. This PR is part of a series of PRs that aim to close flutter#83556. The PR that contains all changes can be found at flutter/packages#4140.
1 parent b5958e2 commit 2c54697

26 files changed

+1185
-362
lines changed

packages/webview_flutter/webview_flutter_android/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## NEXT
1+
## 3.13.0
22

3+
* Adds support for `PlatformNavigationDelegate.setOnHttpAuthRequest`.
34
* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0.
45

56
## 3.12.1

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2523,6 +2523,29 @@ public void doUpdateVisitedHistory(
25232523
Arrays.asList(instanceIdArg, webViewInstanceIdArg, urlArg, isReloadArg)),
25242524
channelReply -> callback.reply(null));
25252525
}
2526+
2527+
public void onReceivedHttpAuthRequest(
2528+
@NonNull Long instanceIdArg,
2529+
@NonNull Long webViewInstanceIdArg,
2530+
@NonNull Long httpAuthHandlerInstanceIdArg,
2531+
@NonNull String hostArg,
2532+
@NonNull String realmArg,
2533+
@NonNull Reply<Void> callback) {
2534+
BasicMessageChannel<Object> channel =
2535+
new BasicMessageChannel<>(
2536+
binaryMessenger,
2537+
"dev.flutter.pigeon.webview_flutter_android.WebViewClientFlutterApi.onReceivedHttpAuthRequest",
2538+
getCodec());
2539+
channel.send(
2540+
new ArrayList<Object>(
2541+
Arrays.asList(
2542+
instanceIdArg,
2543+
webViewInstanceIdArg,
2544+
httpAuthHandlerInstanceIdArg,
2545+
hostArg,
2546+
realmArg)),
2547+
channelReply -> callback.reply(null));
2548+
}
25262549
}
25272550
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
25282551
public interface DownloadListenerHostApi {
@@ -3415,4 +3438,156 @@ public void create(@NonNull Long instanceIdArg, @NonNull Reply<Void> callback) {
34153438
channelReply -> callback.reply(null));
34163439
}
34173440
}
3441+
/**
3442+
* Host API for `HttpAuthHandler`.
3443+
*
3444+
* <p>This class may handle instantiating and adding native object instances that are attached to
3445+
* a Dart instance or handle method calls on the associated native class or an instance of the
3446+
* class.
3447+
*
3448+
* <p>See https://developer.android.com/reference/android/webkit/HttpAuthHandler.
3449+
*
3450+
* <p>Generated interface from Pigeon that represents a handler of messages from Flutter.
3451+
*/
3452+
public interface HttpAuthHandlerHostApi {
3453+
/** Handles Dart method `HttpAuthHandler.useHttpAuthUsernamePassword`. */
3454+
@NonNull
3455+
Boolean useHttpAuthUsernamePassword(@NonNull Long instanceId);
3456+
/** Handles Dart method `HttpAuthHandler.cancel`. */
3457+
void cancel(@NonNull Long instanceId);
3458+
/** Handles Dart method `HttpAuthHandler.proceed`. */
3459+
void proceed(@NonNull Long instanceId, @NonNull String username, @NonNull String password);
3460+
3461+
/** The codec used by HttpAuthHandlerHostApi. */
3462+
static @NonNull MessageCodec<Object> getCodec() {
3463+
return new StandardMessageCodec();
3464+
}
3465+
/**
3466+
* Sets up an instance of `HttpAuthHandlerHostApi` to handle messages through the
3467+
* `binaryMessenger`.
3468+
*/
3469+
static void setup(
3470+
@NonNull BinaryMessenger binaryMessenger, @Nullable HttpAuthHandlerHostApi api) {
3471+
{
3472+
BasicMessageChannel<Object> channel =
3473+
new BasicMessageChannel<>(
3474+
binaryMessenger,
3475+
"dev.flutter.pigeon.webview_flutter_android.HttpAuthHandlerHostApi.useHttpAuthUsernamePassword",
3476+
getCodec());
3477+
if (api != null) {
3478+
channel.setMessageHandler(
3479+
(message, reply) -> {
3480+
ArrayList<Object> wrapped = new ArrayList<Object>();
3481+
ArrayList<Object> args = (ArrayList<Object>) message;
3482+
Number instanceIdArg = (Number) args.get(0);
3483+
try {
3484+
Boolean output =
3485+
api.useHttpAuthUsernamePassword(
3486+
(instanceIdArg == null) ? null : instanceIdArg.longValue());
3487+
wrapped.add(0, output);
3488+
} catch (Throwable exception) {
3489+
ArrayList<Object> wrappedError = wrapError(exception);
3490+
wrapped = wrappedError;
3491+
}
3492+
reply.reply(wrapped);
3493+
});
3494+
} else {
3495+
channel.setMessageHandler(null);
3496+
}
3497+
}
3498+
{
3499+
BasicMessageChannel<Object> channel =
3500+
new BasicMessageChannel<>(
3501+
binaryMessenger,
3502+
"dev.flutter.pigeon.webview_flutter_android.HttpAuthHandlerHostApi.cancel",
3503+
getCodec());
3504+
if (api != null) {
3505+
channel.setMessageHandler(
3506+
(message, reply) -> {
3507+
ArrayList<Object> wrapped = new ArrayList<Object>();
3508+
ArrayList<Object> args = (ArrayList<Object>) message;
3509+
Number instanceIdArg = (Number) args.get(0);
3510+
try {
3511+
api.cancel((instanceIdArg == null) ? null : instanceIdArg.longValue());
3512+
wrapped.add(0, null);
3513+
} catch (Throwable exception) {
3514+
ArrayList<Object> wrappedError = wrapError(exception);
3515+
wrapped = wrappedError;
3516+
}
3517+
reply.reply(wrapped);
3518+
});
3519+
} else {
3520+
channel.setMessageHandler(null);
3521+
}
3522+
}
3523+
{
3524+
BasicMessageChannel<Object> channel =
3525+
new BasicMessageChannel<>(
3526+
binaryMessenger,
3527+
"dev.flutter.pigeon.webview_flutter_android.HttpAuthHandlerHostApi.proceed",
3528+
getCodec());
3529+
if (api != null) {
3530+
channel.setMessageHandler(
3531+
(message, reply) -> {
3532+
ArrayList<Object> wrapped = new ArrayList<Object>();
3533+
ArrayList<Object> args = (ArrayList<Object>) message;
3534+
Number instanceIdArg = (Number) args.get(0);
3535+
String usernameArg = (String) args.get(1);
3536+
String passwordArg = (String) args.get(2);
3537+
try {
3538+
api.proceed(
3539+
(instanceIdArg == null) ? null : instanceIdArg.longValue(),
3540+
usernameArg,
3541+
passwordArg);
3542+
wrapped.add(0, null);
3543+
} catch (Throwable exception) {
3544+
ArrayList<Object> wrappedError = wrapError(exception);
3545+
wrapped = wrappedError;
3546+
}
3547+
reply.reply(wrapped);
3548+
});
3549+
} else {
3550+
channel.setMessageHandler(null);
3551+
}
3552+
}
3553+
}
3554+
}
3555+
/**
3556+
* Flutter API for `HttpAuthHandler`.
3557+
*
3558+
* <p>This class may handle instantiating and adding Dart instances that are attached to a native
3559+
* instance or receiving callback methods from an overridden native class.
3560+
*
3561+
* <p>See https://developer.android.com/reference/android/webkit/HttpAuthHandler.
3562+
*
3563+
* <p>Generated class from Pigeon that represents Flutter messages that can be called from Java.
3564+
*/
3565+
public static class HttpAuthHandlerFlutterApi {
3566+
private final @NonNull BinaryMessenger binaryMessenger;
3567+
3568+
public HttpAuthHandlerFlutterApi(@NonNull BinaryMessenger argBinaryMessenger) {
3569+
this.binaryMessenger = argBinaryMessenger;
3570+
}
3571+
3572+
/** Public interface for sending reply. */
3573+
@SuppressWarnings("UnknownNullness")
3574+
public interface Reply<T> {
3575+
void reply(T reply);
3576+
}
3577+
/** The codec used by HttpAuthHandlerFlutterApi. */
3578+
static @NonNull MessageCodec<Object> getCodec() {
3579+
return new StandardMessageCodec();
3580+
}
3581+
/** Create a new Dart instance and add it to the `InstanceManager`. */
3582+
public void create(@NonNull Long instanceIdArg, @NonNull Reply<Void> callback) {
3583+
BasicMessageChannel<Object> channel =
3584+
new BasicMessageChannel<>(
3585+
binaryMessenger,
3586+
"dev.flutter.pigeon.webview_flutter_android.HttpAuthHandlerFlutterApi.create",
3587+
getCodec());
3588+
channel.send(
3589+
new ArrayList<Object>(Collections.singletonList(instanceIdArg)),
3590+
channelReply -> callback.reply(null));
3591+
}
3592+
}
34183593
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.webviewflutter;
6+
7+
import android.webkit.HttpAuthHandler;
8+
import androidx.annotation.NonNull;
9+
import io.flutter.plugin.common.BinaryMessenger;
10+
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.HttpAuthHandlerFlutterApi;
11+
12+
/**
13+
* Flutter API implementation for {@link HttpAuthHandler}.
14+
*
15+
* <p>This class may handle adding native instances that are attached to a Dart instance or passing
16+
* arguments of callbacks methods to a Dart instance.
17+
*/
18+
public class HttpAuthHandlerFlutterApiImpl {
19+
// To ease adding additional methods, this value is added prematurely.
20+
@SuppressWarnings({"unused", "FieldCanBeLocal"})
21+
private final BinaryMessenger binaryMessenger;
22+
23+
private final InstanceManager instanceManager;
24+
25+
private final HttpAuthHandlerFlutterApi api;
26+
27+
/**
28+
* Constructs a {@link HttpAuthHandlerFlutterApiImpl}.
29+
*
30+
* @param binaryMessenger used to communicate with Dart over asynchronous messages
31+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
32+
*/
33+
public HttpAuthHandlerFlutterApiImpl(
34+
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
35+
this.binaryMessenger = binaryMessenger;
36+
this.instanceManager = instanceManager;
37+
api = new HttpAuthHandlerFlutterApi(binaryMessenger);
38+
}
39+
40+
/**
41+
* Stores the `HttpAuthHandler` instance and notifies Dart to create and store a new
42+
* `HttpAuthHandler` instance that is attached to this one. If `instance` has already been added,
43+
* this method does nothing.
44+
*/
45+
public void create(
46+
@NonNull HttpAuthHandler instance, @NonNull HttpAuthHandlerFlutterApi.Reply<Void> callback) {
47+
if (!instanceManager.containsInstance(instance)) {
48+
api.create(instanceManager.addHostCreatedInstance(instance), callback);
49+
}
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.webviewflutter;
6+
7+
import android.webkit.HttpAuthHandler;
8+
import androidx.annotation.NonNull;
9+
import io.flutter.plugin.common.BinaryMessenger;
10+
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.HttpAuthHandlerHostApi;
11+
import java.util.Objects;
12+
13+
/**
14+
* Host api implementation for {@link HttpAuthHandler}.
15+
*
16+
* <p>Handles creating {@link HttpAuthHandler}s that intercommunicate with a paired Dart object.
17+
*/
18+
public class HttpAuthHandlerHostApiImpl implements HttpAuthHandlerHostApi {
19+
// To ease adding additional methods, this value is added prematurely.
20+
@SuppressWarnings({"unused", "FieldCanBeLocal"})
21+
private final BinaryMessenger binaryMessenger;
22+
23+
private final InstanceManager instanceManager;
24+
25+
/**
26+
* Constructs a {@link HttpAuthHandlerHostApiImpl}.
27+
*
28+
* @param binaryMessenger used to communicate with Dart over asynchronous messages
29+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
30+
*/
31+
public HttpAuthHandlerHostApiImpl(
32+
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
33+
this.binaryMessenger = binaryMessenger;
34+
this.instanceManager = instanceManager;
35+
}
36+
37+
@NonNull
38+
@Override
39+
public Boolean useHttpAuthUsernamePassword(@NonNull Long instanceId) {
40+
return getHttpAuthHandlerInstance(instanceId).useHttpAuthUsernamePassword();
41+
}
42+
43+
@Override
44+
public void cancel(@NonNull Long instanceId) {
45+
getHttpAuthHandlerInstance(instanceId).cancel();
46+
}
47+
48+
@Override
49+
public void proceed(
50+
@NonNull Long instanceId, @NonNull String username, @NonNull String password) {
51+
getHttpAuthHandlerInstance(instanceId).proceed(username, password);
52+
}
53+
54+
private HttpAuthHandler getHttpAuthHandlerInstance(@NonNull Long instanceId) {
55+
return Objects.requireNonNull(instanceManager.getInstance(instanceId));
56+
}
57+
}

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import android.annotation.SuppressLint;
88
import android.os.Build;
9+
import android.webkit.HttpAuthHandler;
910
import android.webkit.WebResourceError;
1011
import android.webkit.WebResourceRequest;
1112
import android.webkit.WebView;
@@ -230,6 +231,26 @@ public void doUpdateVisitedHistory(
230231
getIdentifierForClient(webViewClient), webViewIdentifier, url, isReload, callback);
231232
}
232233

234+
/** Passes arguments from {@link WebViewClient#onReceivedHttpAuthRequest} to Dart. */
235+
public void onReceivedHttpAuthRequest(
236+
@NonNull WebViewClient webViewClient,
237+
@NonNull WebView webview,
238+
@NonNull HttpAuthHandler httpAuthHandler,
239+
@NonNull String host,
240+
@NonNull String realm,
241+
@NonNull Reply<Void> callback) {
242+
new HttpAuthHandlerFlutterApiImpl(binaryMessenger, instanceManager)
243+
.create(httpAuthHandler, reply -> {});
244+
245+
onReceivedHttpAuthRequest(
246+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webViewClient)),
247+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webview)),
248+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(httpAuthHandler)),
249+
host,
250+
realm,
251+
callback);
252+
}
253+
233254
private long getIdentifierForClient(WebViewClient webViewClient) {
234255
final Long identifier = instanceManager.getIdentifierForStrongReference(webViewClient);
235256
if (identifier == null) {

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import android.graphics.Bitmap;
1010
import android.os.Build;
1111
import android.view.KeyEvent;
12+
import android.webkit.HttpAuthHandler;
1213
import android.webkit.WebResourceError;
1314
import android.webkit.WebResourceRequest;
1415
import android.webkit.WebView;
@@ -38,7 +39,7 @@ public static class WebViewClientImpl extends WebViewClient {
3839
/**
3940
* Creates a {@link WebViewClient} that passes arguments of callbacks methods to Dart.
4041
*
41-
* @param flutterApi handles sending messages to Dart
42+
* @param flutterApi handles sending messages to Dart.
4243
*/
4344
public WebViewClientImpl(@NonNull WebViewClientFlutterApiImpl flutterApi) {
4445
this.flutterApi = flutterApi;
@@ -95,6 +96,15 @@ public void doUpdateVisitedHistory(
9596
flutterApi.doUpdateVisitedHistory(this, view, url, isReload, reply -> {});
9697
}
9798

99+
@Override
100+
public void onReceivedHttpAuthRequest(
101+
@NonNull WebView view,
102+
@NonNull HttpAuthHandler handler,
103+
@NonNull String host,
104+
@NonNull String realm) {
105+
flutterApi.onReceivedHttpAuthRequest(this, view, handler, host, realm, reply -> {});
106+
}
107+
98108
@Override
99109
public void onUnhandledKeyEvent(@NonNull WebView view, @NonNull KeyEvent event) {
100110
// Deliberately empty. Occasionally the webview will mark events as having failed to be
@@ -176,6 +186,16 @@ public void doUpdateVisitedHistory(
176186
flutterApi.doUpdateVisitedHistory(this, view, url, isReload, reply -> {});
177187
}
178188

189+
// Handles an HTTP authentication request.
190+
//
191+
// This callback is invoked when the WebView encounters a website requiring HTTP authentication.
192+
// [host] and [realm] are provided for matching against stored credentials, if any.
193+
@Override
194+
public void onReceivedHttpAuthRequest(
195+
@NonNull WebView view, HttpAuthHandler handler, String host, String realm) {
196+
flutterApi.onReceivedHttpAuthRequest(this, view, handler, host, realm, reply -> {});
197+
}
198+
179199
@Override
180200
public void onUnhandledKeyEvent(@NonNull WebView view, @NonNull KeyEvent event) {
181201
// Deliberately empty. Occasionally the webview will mark events as having failed to be

0 commit comments

Comments
 (0)