Flutter: Firebase auth から twitter サインインを行う。AndroidとiPhone
1.ながれ
Flutterアプリ用の部品集め、バックエンドは、Firebaseを使用する。
Google サインインを実現したので、twitterを試す。
firebase_auth プラグイン で全部行けるかと思ったら、Firebaseにかかる部分のみで、twitterからアクセストークンをもらうあたりの処理は範囲外のよう。
flutter_twitter_login プラグインを試したが、内部で非推奨のjava apiを利用しているというログが出ることと、公式プラグインでないことから、対処をググる中で、Stackoverflowのコメントを参考に処理を実装することとする。
動いたところ(Android/iPhone)
2.準備
2.1 twitter
以下のサイトから、アプリの登録を行い、アプリケーションの詳細 – Key and Tokens から、API key と API secret key を発行する。
Twitter Developer
2.2 Firebase Console
Firebaseが、アプリケーションのバックエンドとして、リダイレクト先のURLやらを準備してくれる。
プロジェクトから、Authentication 、Sign in method から twitter を選択
2.1で取得した、API Key と API secret key を登録、有効にする。
3.実装
3.1 呼び出し側
- Tiwtter Sign In ボタン押下で、上記2で取得したAPIキーを、TwitterSiginInPageに引き渡す
- 画面遷移を終え、戻ってくると、credential に、トークンが含まれている
- 取得したトークンをFirebase authに渡して、ユーザー情報を取得し画面表示
lib\firebase_auth.dart
import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; import './twitter_sign_in.dart'; class FirebaseAuthPage extends StatefulWidget { @override State<StatefulWidget> createState() => FIrebaseAuthPageState(); } enum Menu { twitter_sign_in, item2, } class FIrebaseAuthPageState extends State<FirebaseAuthPage> { final FirebaseAuth _auth = FirebaseAuth.instance; String _userAvatarUrl = ''; String _userDisplayName = ''; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Firebase Auth Sample"), ), body: Column( children: <Widget>[ PopupMenuButton( onSelected: popupMenuSelected, itemBuilder: (BuildContext context) => <PopupMenuEntry<Menu>>[ const PopupMenuItem( child: const ListTile( leading:Icon(Icons.account_box), title:Text("Twitter Sign In")), value: Menu.twitter_sign_in), const PopupMenuItem( child: const ListTile( leading:Icon(Icons.account_circle), title:Text("item2")), value: Menu.item2), ], ), ListTile(leading : CircleAvatar( backgroundImage: NetworkImage(_userAvatarUrl), ), title:Text(_userDisplayName ?? ''),), ], ), ); } void popupMenuSelected(Menu selectedMenu){ switch(selectedMenu) { case Menu.twitter_sign_in: _pushTwitterPage(context); break; default: break; } } void _pushTwitterPage(BuildContext context) async { Widget page = TwitterSignInPage( consumerKey: "***** your api key ****", consumerSecret: "***** your api secret key ****", oauthCallbackHandler: "https://**** firebase call back url ****"); final credential = await Navigator.of(context).push( MaterialPageRoute<AuthCredential>(builder: (_) => page) ); FirebaseUser user; if (credential != null) { user = (await _auth.signInWithCredential(credential)).user; } setState(() { if (user != null) { _userAvatarUrl = user.photoUrl; _userDisplayName = user.displayName; } else { _userDisplayName = 'Failed to sign in with Twitter. '; } }); } // void _twitterSignOut() async { // _auth.signOut(); // } }
3.2 twitter ログイン主処理
5年ほど前5年ほど前にも、Djangoで同じことをやっていた。以下を参考にSTEP1~3を実装
STEP1
STEP2
STEP3
lib/twitter_sign_in.dart
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; import 'package:oauth1/oauth1.dart'; import 'package:firebase_auth/firebase_auth.dart'; /// /// https://stackoverflow.com/questions/60461547/how-to-sign-in-with-twitter-using-firebase-auth-with-flutter /// class TwitterSignInPage extends StatefulWidget { final twitterPlatform = Platform( 'https://api.twitter.com/oauth/request_token', // temporary credentials request 'https://api.twitter.com/oauth/authorize', // resource owner authorization 'https://api.twitter.com/oauth/access_token', // token credentials request SignatureMethods.hmacSha1, // signature method ); final ClientCredentials clientCredentials; final String oauthCallbackHandler; TwitterSignInPage({ @required final String consumerKey, @required final String consumerSecret, @required this.oauthCallbackHandler, }) : clientCredentials = ClientCredentials(consumerKey, consumerSecret); @override State<StatefulWidget> createState() => TwitterSignInPageState(); } enum Menu {twitter_sign_in, twitter_sign_out} class TwitterSignInPageState extends State<TwitterSignInPage> { final flutterWebviewPlugin = FlutterWebviewPlugin(); Authorization _oauth; @override void initState() { super.initState(); // Twitter OAuth の初期化 _oauth = Authorization(widget.clientCredentials, widget.twitterPlatform); flutterWebviewPlugin.onUrlChanged.listen((url) { // STEP2のコールバックを、STEP3へ移動するために監視 if (url.startsWith(widget.oauthCallbackHandler)) { final queryParameters = Uri.parse(url).queryParameters; final oauthToken = queryParameters['oauth_token']; final oauthVerifier = queryParameters['oauth_verifier']; if (null != oauthToken && null != oauthVerifier) { _twitterLogInFinish(oauthToken, oauthVerifier); } } }); _twitterLogInStart(); } @override void dispose() { flutterWebviewPlugin.dispose(); super.dispose(); } Future<void> _twitterLogInStart() async { assert(null != _oauth); // STEP1:リクエストトークンの入手 // oauth/request_tokenをPOST送信することで、リクエストトークンを入手 // oauth_callbackは、STEP2が成功したときにリダイレクトされるURL final requestTokenResponse = await _oauth.requestTemporaryCredentials(widget.oauthCallbackHandler); // STEP2: 認証ページへのリダイレクト // oauth/authenticate のGETリクエストに、STEP1で入手したトークンを、oauth_tokenとして渡す final authorizationPage = _oauth.getResourceOwnerAuthorizationURI( requestTokenResponse.credentials.token); flutterWebviewPlugin.reloadUrl(authorizationPage); } Future<void> _twitterLogInFinish( String oauthToken, String oauthVerifier) async { // STEP3: リクエストトークンをアクセストークンに変換 // リクエストトークンをアクセストークンに書換えるのに、アプリケーションはPOST oauth/access_token へ STEP2で入手した、 // oauth_verifier POSTリクエストを送信する必要がある。 // リクエストトークンは、oauth_token ヘッダーにも渡されるが、これは署名プロセスに追記される final tokenCredentialsResponse = await _oauth.requestTokenCredentials( Credentials(oauthToken, ''), oauthVerifier); final result = TwitterAuthProvider.getCredential( authToken: tokenCredentialsResponse.credentials.token, authTokenSecret: tokenCredentialsResponse.credentials.tokenSecret, ); print("result $result"); Navigator.pop(context, result); } @override Widget build(BuildContext context) { return WebviewScaffold( appBar: AppBar(title: Text("Twitter Login")), url: "https://twitter.com", ); } }
以上。