【Flutter】Webviewに認証情報を渡す
webview_flutter で認証情報を付与して Web ページを表示したく、色々と調査してみました。
前提
webview_flutter の基本的な使い方については記載しません。
他の有益な記事が沢山ございますので、基本的な使用方法についてはそちらを参照ください。
【検証環境】
flutter doctor
[✓] Flutter (Channel stable, 3.22.2, on macOS 14.0 23A344 darwin-arm64, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 15.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2024.1)
[✓] VS Code (version 1.95.1)
[✓] Connected device (6 available)
[✓] Network resources
• No issues found!
headers
controller = WebViewController()
...loadRequest(Uri.parse('https://flutter.dev'), headers: {});
WebViewController.loadRequest
のオプショナルの引数でheaders
プロパティが存在するが、android には対応していないと Readme に記載あり。
なので、クロスプラットフォームである以上headers
は実質使えない。
(回避策もありそうだが、できれば platform 毎に分けたくない。)
Readme: Setting custom headers on POST requests
Currently, setting custom headers when making a post request with the WebViewController's loadRequest method is not supported on Android. If you require this functionality, a workaround is to make the request manually, and then load the response data using loadHtmlString instead.
window.localStorage
WebViewControllerのrunJavascript
やrunJavascriptReturningResult
を使用すれば webview 上で javascript を実行することができる。
ブラウザの localStorage [1]に認証情報を保存し、webview で表示する際に localStorage
から認証情報を取得する方法が考えられる。
final accessToken = "your_access_token";
// ...省略
final controller = WebViewController();
await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
await controller.loadRequest("url");
// 1.アクセストークンをlocalStorageに保存
// 2.localStorageからアクセストークンを取得し、確認用でダイアログで表示
controller.runJavaScript("""
window.localStorage.setItem('access_token', '$accessToken');
const token = window.localStorage.getItem('access_token');
window.confirm('取得したアクセストークン: ' + token);
""");
上記の設定で、android で webview を表示すると、ダイアログが表示されて認証情報が localStorage に保存されていることが確認できる。
ただ、ios で実行すると、javascript が実行されずに終了してしまう。(上記のようなモーダルが出ない。)
確認1
Issue: [WebView] Provide webview_flutter a way to enable localStorage より、onPageFinished
の中で実行するようにしてみたりしたが、特に変化なく ios だと javascript が実行されない。
memo
以下、android だと意図通り javascript が実行されるが、ios だと実行されない。
final isSetToken = useState(false);
final accessToken = "your_access_token";
// ...省略
final controller = WebViewController();
await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
await controller.loadRequest("url");
await controller.setNavigationDelegate(
NavigationDelegate(
onPageFinished: (url) {
if (isSetToken.value) return;
// 1.アクセストークンをlocalStorageに保存
// 2.localStorageからアクセストークンを取得し、確認用でダイアログで表示
controller.runJavaScript("""
window.localStorage.setItem('access_token', '$accessToken');
const token = window.localStorage.getItem('access_token');
window.confirm('取得したアクセストークン: ' + token);
""");
// 試しに runJavaScriptReturningResult で実行してみても結果は変わらず。
Future.delayed(
const Duration(milliseconds: 10),() async {
final result = await controller.runJavaScriptReturningResult("""
window.localStorage.setItem('access_token', '$accessToken');
const token = window.localStorage.getItem('access_token');
window.confirm('取得したアクセストークン: ' + token);
""");
print('webview javascript result: $result');
// localStorageにトークンをセットした後、ページを再読み込みする。
isSetToken.value = true;
// await controller.reload(); // ← reload関数もあるが、適切に制御しないと無限ループになる可能性があるため注意。
},
));
確認2
Issue の[webview_flutter] Can not invoke Javascript function on iOSより、なにやら iOS だと javascript の実行ができないやらなんやらと記載がある。
が、解消せず。
確認3
ドキュメント Readme のPlatform-Specific Featuresセクションの内容や、上記の Issue[WebView] Provide webview_flutter a way to enable localStorage にも記載ある Platform 固有の設定を適切に設定したら ios でも javascript が実行されるかもしれない。
が、未検証。(できるだけ固有の設定をしたくなかっため。)
確認4
ios 側の権限設定が足りないのか?と検索して出てきたそれっぽい権限(NSAllowsArbitraryLoads や NSAllowsArbitraryLoadsInWebContent)を info.plist に追加してみたが、改善なし。
参考 Issue:
cookie
Webview で表示する際に cookie に証情情報を付与してみる。
webview_flutter で定義されているWebviewCookie
とWebViewCookieManager
を使用。
final accessToken = "your_access_token";
// ...省略
final controller = WebViewController();
await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
await controller.loadRequest("url");
// cookie設定
final cookie = WebViewCookie(
name: 'access_token',
value: accessToken ?? '',
domain: "<domain URL>", // 設定するURLに注意
);
await WebViewCookieManager().setCookie(cookie);
これだけで cookie の設定が完了。
ただ、WebViewCookieManager
では、setCookie()
とclearCookies()
ぐらいしか関数が提供されていないため、本当に設定されたかどうかが確認できない。
.runJavaScript
で確認しようにも ios だと実行されないので。
(android なら javascript のdocument.cookieで取得確認可能。)
今回は確認用にwebview_cookie_manager を使用して、設定した cookie を取得確認してみる。
webview_cookie_manager には cookie を扱うための関数がいくつか提供されている。
final controller = WebViewController();
await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
await controller.loadRequest("url");
// cookie設定
final cookie = WebViewCookie(
name: 'access_token',
value: accessToken ?? '',
domain: "<domain URL>",
);
await WebViewCookieManager().setCookie(cookie);
await controller.setNavigationDelegate(
NavigationDelegate(
onPageFinished: (url) async {
// 以下確認用
final cookies = await WebviewCookieManager().getCookies(url);
print('get cookies: $cookies');
},
),
);
上記だとonPageFinished
時に cookie が取得できて出力されることを確認できる。
get cookies: [access_token=your_access_token; Domain=flutter.dev; Path=/, ...省略]
この方法だと Platform 毎のややこしい設定しなくても認証情報を webview に渡すことができる。
NCDC株式会社( ncdc.co.jp/ )のエンジニアチームです。 募集中のエンジニアのポジションや、採用している技術スタックの紹介などはこちら( github.com/ncdcdev/recruitment )をご覧ください! ※エンジニア以外も記事を投稿することがあります
Discussion