前提・実現したいこと
前回の質問 flutterでflutter_appauthプラグインを使ってoauthログインを実装したい2 からの続きの質問です。
アプリから、認証サーバのログインページを呼び出してログインし、認証が済んだら
アプリに戻ってアクセストークンでAPIを取得しようとしています。
認証サーバは自前で用意したものを使います。(FirebaseやSNS認証などは使わない)
今回はKeycloakを使い、DockerのコンテナでローカルPCに立ち上げています。
そのため、以下のプラグインを使用して実装しようとしています。
こちらのGitHubのサンプルコードを参照して実装しました。
MaikuB/flutter_appauth
発生している問題・エラーメッセージ
困っていることその①
アプリから、認証サーバのログインページを呼び出してログインし、認証済みになるところまでは動作するのですが
callback画面からアプリに制御が戻らず、エラーになっています。
④ 認証成功した後のcallback画面(戻るリンクは機能していない)
⑥エラーメッセージ(④の画面を手動で閉じたので、ソースコード中のresultに何も入らずエラーになった
エラー後もアプリは動いているが、その後の画面遷移はしていない
Launching lib/main.dart on Android SDK built for x86 64 in debug mode... ✓ Built build/app/outputs/apk/debug/app-debug.apk. I/flutter ( 5095): 1 I/flutter ( 5095): 2 I/flutter ( 5095): e=PlatformException(authorize_and_exchange_code_failed, Failed to authorize: [error: null, description: User cancelled flow], null)
該当のソースコード
dart
1main.dart 2 3import 'package:flutter/material.dart'; 4import 'package:http/http.dart' as http; 5import 'package:flutter_appauth/flutter_appauth.dart'; 6import 'package:flutter_auth_login_app/screen1.dart'; 7 8void main() => runApp(MyApp()); 9 10class MyApp extends StatelessWidget { 11 12 Widget build(BuildContext context) { 13 return MaterialApp( 14 title: 'Flutter Demo', 15 theme: ThemeData( 16 primarySwatch: Colors.blue, 17 ), 18 home: MyHomePage(title: 'Flutter Demo Home Page'), 19 ); 20 } 21} 22 23class MyHomePage extends StatefulWidget { 24 MyHomePage({Key key, this.title}) : super(key: key); 25 final String title; 26 27 _MyHomePageState createState() => _MyHomePageState(); 28} 29 30class _MyHomePageState extends State<MyHomePage> { 31 final String _clientId = 'test001'; 32 final String _redirectUrl = 'http://10.0.2.2:10081/…/callback.php' ; // callbackのURL 33 34 final List<String> _scopes = <String>[ 35 'openid', 36 'offline_access', 37 ]; 38 39// String _codeVerifier; 40// String _authorizationCode; 41 String _accessToken; 42 String _refreshToken; 43 String _userInfo = ''; 44 45 final FlutterAppAuth _appAuth = FlutterAppAuth(); 46// final TextEditingController _authorizationCodeTextController = TextEditingController(); 47 final TextEditingController _idTokenTextController = TextEditingController(); 48 final TextEditingController _accessTokenExpirationTextController = TextEditingController(); 49 final TextEditingController _accessTokenTextController = TextEditingController(); 50 final TextEditingController _refreshTokenTextController = TextEditingController(); 51 52 final AuthorizationServiceConfiguration _serviceConfiguration = 53 AuthorizationServiceConfiguration( 54 'http://10.0.2.2:18080/…/auth', // 認証エンドポイントのURL 55 'http://10.0.2.2:18080/…/token' // トークンエンドポイントURL 56 ); 57 58 59 void initState() { 60 super.initState(); 61 } 62 63 void _loginFunc() async { 64 // 認証とアクセス、ID、リフレッシュトークン取得を一度にやる場合 65 print('1'); 66 try { 67 print('2'); // ここまで流れて↓のresultに値が戻らないため、エラーになる 68 final AuthorizationTokenResponse result = 69 await _appAuth.authorizeAndExchangeCode( 70 AuthorizationTokenRequest( 71 _clientId, 72 _redirectUrl, 73 serviceConfiguration: _serviceConfiguration, 74 scopes: _scopes), 75 ); 76 77 print('3'); 78 // アクセス、ID、リフレッシュトークン格納 79 if (result != null) { 80 _accessToken = _accessTokenTextController.text = result.accessToken; 81 _idTokenTextController.text = result.idToken; 82 _refreshToken = _refreshTokenTextController.text = result.refreshToken; 83 _accessTokenExpirationTextController.text = result.accessTokenExpirationDateTime?.toIso8601String(); 84 } 85 86 // API取得 87 print('4=$result'); 88 if (result != null) { 89 print('4 in'); 90 await _testApi(result); 91 } 92 93 // 画面遷移 94 print('5'); 95 _gotoScreen1(); 96 97 // エラー処理 98 } catch(e) { 99 print('e=$e'); //エラー時のメッセージ出力 100 } 101 } 102 103 // API取得 104 Future<void> _testApi(TokenResponse response) async { 105 final http.Response httpResponse = await http.get( 106 'http://10.0.2.2:18082/…/data', // APIのエンドポイント 107 headers: <String, String>{'Authorization': 'Bearer $_accessToken'}); 108 setState(() { 109 _userInfo = httpResponse.statusCode == 200 ? httpResponse.body : ''; 110 }); 111 } 112 113 // 画面遷移 screen1 API取得後に遷移する画面 114 void _gotoScreen1() { 115 Navigator.pushReplacement( 116 context, 117 new MaterialPageRoute<Null>( 118 settings: const RouteSettings(name: "/screen1"), 119 builder: (BuildContext context) => Screen1(api:_userInfo), 120 ), 121 ); 122 } 123 124 // widget 125 126 Widget build(BuildContext context) { 127 128 return Scaffold( 129 appBar: AppBar(title: Text(widget.title),), 130 body: Center( 131 132 child: Column( 133 mainAxisAlignment: MainAxisAlignment.center, 134 children: <Widget>[ 135 136 Padding( 137 padding: EdgeInsets.only(bottom: 40), 138 child: Text('アプリ初期画面'), 139 ), 140 141 FlatButton(key:null, onPressed: _loginFunc, 142 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(3.0)), 143 color: Color(0xFF4c6cb3), // 群青色 144 child: Text('ログインページへ',), 145 ) 146 ], 147 ), 148 ), 149 ); 150 } 151} 152 153
dart
1screen1.dart 2 3import 'package:flutter/material.dart'; 4import 'package:http/http.dart' as http; 5import 'package:flutter_appauth/flutter_appauth.dart'; 6 7//// widget //// 8class Screen1 extends StatefulWidget { 9 String api; 10 11 Screen1({Key key, this.api}): super(key: key); 12 13 14 _Screen1State createState() => _Screen1State(this.api); 15} 16 17class _Screen1State extends State<Screen1> { 18 String _api; 19 20 _Screen1State(String api){ 21 this._api = api; 22 } 23 24 25 void initState() { 26 super.initState(); 27 } 28 29 30 Widget build(BuildContext context) { 31 32 return Scaffold( 33 resizeToAvoidBottomInset: false, 34 appBar: AppBar(title: Text('screen1'),), 35 body: Column( 36 children: [ 37 Text('screen1'), 38 Text('$_api'), 39 ] 40 ) 41 ); 42 } 43}
php
1callback.php 2 3 // webアプリの名残でphpファイルに書いてあるが、php部分は無し。戻るリンクの中身は無い。 4 認証成功<br> 5 <a href="">戻る</a> 6
試したこと
なぜ引数を渡してもresultが返ってこないのかを調べるため
_loginFunc()の認証する部分↓の中で
final AuthorizationTokenResponse result = await _appAuth.authorizeAndExchangeCode( AuthorizationTokenRequest( _clientId, // php-test _redirectUrl, serviceConfiguration: _serviceConfiguration, scopes: _scopes), );
何が行われているのか確認しようとしましたが、どうやって確認したら良いか分からない状態です。
困っていることその②
参照したGitのサンプルコード MaikuB/flutter_appauth の中には、flutter_appauth だけでなく
flutter_appauth_platform_interfaceの実装も含まれていましたが、この部分がどういう役割をしているのか、flutter_appauth の main.dart からどのように参照されて使われているのかが分かりません。
そのため、自分の書いた上記のコードにはflutter_appauth_platform_interfaceの実装が含まれていないので
これが原因なのかなと思いますが、使い方が分からず困っています。
補足情報(FW/ツールのバージョンなど)
macOS Mojave
Flutter 1.12.13+hotfix.5
Tools • Dart 2.7.0
説明が長くなってしまいましたが、どうぞよろしくお願いいたします。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。