[Flutter]url_lanucherとuni_linksでOAuth
この記事の補足記事になります。
認証部分は読んだ前提で話を進めるので、読んでください!
webviewを使ったGoogleのOAuthがセキュリティ的にちょっとアレなため、url_launcherとuni_linksを使用した正攻法(?)で実装してみます。
サンプルのリポジトリは同じです。
使用するFlutterパッケージ
- url_launcher : 端末のデフォルトブラウザでWebページを開く
- uni_links : deeplinkするやつ
実装
修正箇所は以下の2点です。
- 「連携」ボタンクリック時にnotionの認証ページをurl_launcherで開くようにする
- uni_liksの
linkStream.listen
で自分のアプリへのリクエストをlistenする
修正が必要なのはmain.dart
のみです。
まず連携ボタンを修正。
child: ElevatedButton(
onPressed: () async {
- webviewNotifier.show();
+ if (await canLaunchUrl(Uri.parse(Env.notionOauthUrl))) {
+ launchUrl(Uri.parse(Env.notionOauthUrl));
+ }
},
child: const Text('Notionと連携する'),
)
次にlistenerを設定
MyAppをConsumerWidgetからConsumerStatefulWidgetに変更します。
linkStream.listen
内で、自分のアプリに対するリクエストで、URLにcode
パラメータを含むものを検知してauthenticateメソッドを実行し、providerを更新します。
class MyApp extends ConsumerStatefulWidget {
const MyApp({super.key});
ConsumerState<MyApp> createState() => _MyApp();
}
class _MyApp extends ConsumerState<MyApp> {
StreamSubscription? _sub;
void initState() {
super.initState();
initUniLinks();
}
Future<void> initUniLinks() async {
_sub = linkStream.listen((link) async {
if (link != null &&
link.startsWith('notionsample://oauth/callback?code')) {
await ref.read(notionOauthApiProvider).authenticate(link);
ref.invalidate(notionAuthProvider);
}
}, onError: (e) {
print(e.toString());
});
}
// build以降はそのまま
}
これで一応実装できました。
動かしてみるとこんな感じ。
2つほど気になるところが...
1. 二度目以降の連携では最初に連携をしたアカウントの連携ページに飛ばされてしまう
ブラウザに初回ログイン時の情報が残っているため?
webviewでは毎回ページを開く前にキャッシュを削除していた(clearCache: true
に設定)が、url_launcherではできないみたい。
Githubに同様のissueがあったが、
url_launcher is intended to have a minimal API for launching, and the API of SFViewController is extremely limited so this couldn't be implemented on iOS anyway. Use cases that need detailed control of the webview should build on webview_flutter instead.
Closing as out of scope.
webviewを使わないと無理っぽい。
2. 認証完了後に自動でブラウザを閉じることができない
ので、ユーザーに手動で閉じてもらう必要がある。
こちらちょっとめんどい程度の問題なのでまあ許せるが、1つ目は今のところ解決策が思い浮かばない。
ということで、いい解決方法知ってる人、教えてください!
Discussion