前提・実現したいこと
前回の質問 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)
該当のソースコード
main.dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_appauth/flutter_appauth.dart';
import 'package:flutter_auth_login_app/screen1.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final String _clientId = 'test001';
final String _redirectUrl = 'http://10.0.2.2:10081/…/callback.php' ; // callbackのURL
final List<String> _scopes = <String>[
'openid',
'offline_access',
];
// String _codeVerifier;
// String _authorizationCode;
String _accessToken;
String _refreshToken;
String _userInfo = '';
final FlutterAppAuth _appAuth = FlutterAppAuth();
// final TextEditingController _authorizationCodeTextController = TextEditingController();
final TextEditingController _idTokenTextController = TextEditingController();
final TextEditingController _accessTokenExpirationTextController = TextEditingController();
final TextEditingController _accessTokenTextController = TextEditingController();
final TextEditingController _refreshTokenTextController = TextEditingController();
final AuthorizationServiceConfiguration _serviceConfiguration =
AuthorizationServiceConfiguration(
'http://10.0.2.2:18080/…/auth', // 認証エンドポイントのURL
'http://10.0.2.2:18080/…/token' // トークンエンドポイントURL
);
@override
void initState() {
super.initState();
}
void _loginFunc() async {
// 認証とアクセス、ID、リフレッシュトークン取得を一度にやる場合
print('1');
try {
print('2'); // ここまで流れて↓のresultに値が戻らないため、エラーになる
final AuthorizationTokenResponse result =
await _appAuth.authorizeAndExchangeCode(
AuthorizationTokenRequest(
_clientId,
_redirectUrl,
serviceConfiguration: _serviceConfiguration,
scopes: _scopes),
);
print('3');
// アクセス、ID、リフレッシュトークン格納
if (result != null) {
_accessToken = _accessTokenTextController.text = result.accessToken;
_idTokenTextController.text = result.idToken;
_refreshToken = _refreshTokenTextController.text = result.refreshToken;
_accessTokenExpirationTextController.text = result.accessTokenExpirationDateTime?.toIso8601String();
}
// API取得
print('4=$result');
if (result != null) {
print('4 in');
await _testApi(result);
}
// 画面遷移
print('5');
_gotoScreen1();
// エラー処理
} catch(e) {
print('e=$e'); //エラー時のメッセージ出力
}
}
// API取得
Future<void> _testApi(TokenResponse response) async {
final http.Response httpResponse = await http.get(
'http://10.0.2.2:18082/…/data', // APIのエンドポイント
headers: <String, String>{'Authorization': 'Bearer $_accessToken'});
setState(() {
_userInfo = httpResponse.statusCode == 200 ? httpResponse.body : '';
});
}
// 画面遷移 screen1 API取得後に遷移する画面
void _gotoScreen1() {
Navigator.pushReplacement(
context,
new MaterialPageRoute<Null>(
settings: const RouteSettings(name: "/screen1"),
builder: (BuildContext context) => Screen1(api:_userInfo),
),
);
}
// widget
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title),),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 40),
child: Text('アプリ初期画面'),
),
FlatButton(key:null, onPressed: _loginFunc,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(3.0)),
color: Color(0xFF4c6cb3), // 群青色
child: Text('ログインページへ',),
)
],
),
),
);
}
}
screen1.dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_appauth/flutter_appauth.dart';
//// widget ////
class Screen1 extends StatefulWidget {
String api;
Screen1({Key key, this.api}): super(key: key);
@override
_Screen1State createState() => _Screen1State(this.api);
}
class _Screen1State extends State<Screen1> {
String _api;
_Screen1State(String api){
this._api = api;
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(title: Text('screen1'),),
body: Column(
children: [
Text('screen1'),
Text('$_api'),
]
)
);
}
}
callback.php
// webアプリの名残でphpファイルに書いてあるが、php部分は無し。戻るリンクの中身は無い。
認証成功<br>
<a href="">戻る</a>
試したこと
なぜ引数を渡しても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
説明が長くなってしまいましたが、どうぞよろしくお願いいたします。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
0
追記:
解決はしていないのですが、色々試したところ現象が変化し
困っている内容が変わったため、こちらの質問を一旦クローズし、新たに質問を立て直させて頂きます
○現在の状況
困っていることその①について
認証後にアプリに戻ってこない → 戻ってくるようにはなったが、直後にアプリが落ちる(改めて別の質問を立てます)。
アプリに戻ってくる部分の解決方法は、前の質問
flutterでflutter_appauthプラグインを使ってoauthログインを実装したい2 の自己解決の追記に書きました。
困っていることその②
サンプルコードのflutter_appauth_platform_interfaceの実装がなぜあるのか分からない。
flutter_appauth_platform_interfaceは、flutter_appauthから更に呼ばれているプラグインなので
実装部分は書かなくても動く(自分のアプリはそう)のではないかと思うのですが
なぜこのサンプルコードに実装部分が含まれているのかが分からない。
まだ分からない部分について、解決方法ご存知の方がいれば、引き続きご意見をお待ちしています。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.34%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる