質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.21%

flutterでflutter_appauthプラグインを使ってoauthログインを実装したい2

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 812

sagme

score 9

前提・実現したいこと

表題の件について
デモ用に用意されていた認証サイトにログインし、自分のアプリに戻ってくるコードをかきましたが下記の2点で困っています。
1. アプリの画面遷移の動作が意図と違っているので変えたい
2. デモ用の認証サイトではなく別の認証サイトに実装を変更したいが、分からない。

下記リンクflutter_appauth 0.8.1のページのExampleと
flutter_appauth 0.8.1

GitHubのコードを参照して実装しました。
MaikuB/flutter_appauth

認証サイトとログインページはこちらです。
デモサイト
ログインページ

●現状のアプリの動作

アプリ起動時に表示される初期画面の「ログインページへ」ボタンを押すと、ログインページにリダイレクトして
そこであらかじめ用意されているユーザアカウントでログインすると
認証済みとなり、自分のアプリに戻ってきます。

① アプリ初期画面
イメージ説明
② リダイレクト画面
イメージ説明
③ リダイレクト画面にユーザID,PWを入力 bob/bob
イメージ説明
④ 認証成功
イメージ説明
⑤ 自動で自分のアプリの初期画面に戻る(ここが意図と違う動き)
イメージ説明
⑥ screen1に画面遷移(取得したAPIの内容を表示)
イメージ説明

発生している問題・エラーメッセージ

困っていること 1. について
画面遷移①〜⑥の流れで、認証成功したら前の初期画面に自動で戻ってしまう(⑤の動き)のを
④認証成功したら⑥screen1に直接遷移したいのですが
どうやったら遷移先が変更できるのかが分かりません。

困っていること 2. について
プラグインを使って認証する時に、3つのURLを指定するのですが
認証サイトを変更した場合は、何を指定したら良いのかが分かりません。

指定するのは下記の実装部分の、_redirectUrlに1つと_serviceConfigurationに2つ指定するURLの合計3つです
特に_redirectUrlは、デモサイトのURL(demo.identityserver.io)の逆さまにしたもの(io.identityserver.demo)
プラス :/oauthredirect という文字列になっていますが
これは、サイトが変わっても、認証サーバのURLを逆さまにしたもの + :/oauthredirect を
指定すれば良いのでしょうか?
そもそもなぜ逆さまにするのか、実際にリダイレクトされるログインページのURLと異なる文字列を指定して
ちゃんと遷移するのは何故なんでしょうか?

final AuthorizationTokenResponse result =
        await _appAuth.authorizeAndExchangeCode(
      AuthorizationTokenRequest(
        _clientId,                                     // 'native.code'
        _redirectUrl,                                  // 'io.identityserver.demo:/oauthredirect'
        serviceConfiguration: _serviceConfiguration,  // 'https://demo.identityserver.io/connect/authorize',
                                                      // 'https://demo.identityserver.io/connect/token';

        scopes: _scopes),
    );

該当のソースコード

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 = 'native.code';
  final String _redirectUrl = 'io.identityserver.demo:/oauthredirect';
//  final String _discoveryUrl = 'https://demo.identityserver.io/.well-known/openid-configuration';

  final List<String> _scopes = <String>[
    'openid',
    'profile',
    'email',
    'offline_access',
    'api'
  ];

  String _accessToken;
  String _refreshToken;
  String _userInfo = '';

  final FlutterAppAuth _appAuth = FlutterAppAuth();
  final TextEditingController _idTokenTextController = TextEditingController();
  final TextEditingController _accessTokenExpirationTextController = TextEditingController();
  final TextEditingController _accessTokenTextController = TextEditingController();
  final TextEditingController _refreshTokenTextController = TextEditingController();

  final AuthorizationServiceConfiguration _serviceConfiguration =
    AuthorizationServiceConfiguration(
      'https://demo.identityserver.io/connect/authorize',
      'https://demo.identityserver.io/connect/token'
    );

  @override
  void initState() {
    super.initState();

  }

  void _loginFunc() async {
    // 認証コード無しでアクセス、ID、リフレッシュトークンを取ってくる場合
    // show that we can also explicitly specify the endpoints rather than getting from the details from the discovery document
    // 発見ドキュメントの詳細から取得するのではなく、エンドポイントを明示的に指定することもできることを示します
    final AuthorizationTokenResponse result =
        await _appAuth.authorizeAndExchangeCode(
      AuthorizationTokenRequest(
        _clientId, 
        _redirectUrl,
        serviceConfiguration: _serviceConfiguration,  // 'https://demo.identityserver.io/connect/authorize',
                                                      // 'https://demo.identityserver.io/connect/token');
        scopes: _scopes),
    );

    // アクセス、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('1=$result');
    if (result != null) {
      print('1');
      await _testApi(result);         // API取ってくる
    }
    // 画面遷移
    _gotoScreen1();
  }


  // API取得
  Future<void> _testApi(TokenResponse response) async {
    print('token=$_accessToken');
    final http.Response httpResponse = await http.get(
        'https://demo.identityserver.io/api/test',
        headers: <String, String>{'Authorization': 'Bearer $_accessToken'});
    setState(() {
      _userInfo = httpResponse.statusCode == 200 ? httpResponse.body : '';
  //    _isBusy = false;
    });
    print('_userInfo=$_userInfo');
  }

  // 画面遷移 screen1 初期画面に戻らない
  void _gotoScreen1() {
  //  Navigator.push(
    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, // _gotoScreen1 _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'),
        ]
      )
    );
  }
}

試したこと

同じプラグインを使用した実装例がないか探してみましたが
探し出せませんでした。
特に_redirectUrlの部分をどうしたらいいか分からず、別の実装を試すことが出来ない状態です。

補足情報(FW/ツールのバージョンなど)

macOS Mojave
Flutter 1.12.13+hotfix.5
Tools • Dart 2.7.0

どうぞよろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

0

追記:
解決はしていないのですが、色々試したところ現象が変化し
困っている内容が変わったため、こちらの質問を一旦クローズし、新たに質問を立て直させて頂きます。

○現在の状況
困っていること 1. について
認証成功した後に一旦前の画面に戻ってしまう動作については、とりあえず保留にしておきます。
(遷移先を変更できるなら尚良しというスタンス)

困っていること 2. について
_redirectUrlに自前で用意した認証サーバで指定しているredirect URLを指定し、_serviceConfigurationに指定する2つのURLは、それぞれ用意した
認証エンドポイントとトークンエンドポイントのURLを指定したところ
自前の認証サーバのログイン画面を表示し、認証してリダイレクトするところまでは進みました。ただ
callback先からアプリに戻ってこないため、そこで困っています。

追記:
_redirectUrlに指定するURLについて、カスタムスキーム + :/oauthredirect を指定すると
リダイレクト先として、認証後に元のアプリに戻るようです。

カスタムスキームを使用する設定として、 android/app/build.gradle と、 ios/Runner/info.plist に
同じカスタムスキームを設定しておくことと
認証サーバ側でリダイレクトを許可するURLの指定に カスタムスキーム + :/oauthredirect を
設定することが必要です。
 
 
 
別に質問を立てましたが、上記の質問も解決方法ご存知の方がいれば、引き続きご意見をお待ちしています。

内容を修正した次の質問を立てましたので、よろしければそちらもご参照お願いします。
flutterでflutter_appauthプラグインを使ってoauthログインを実装したい3

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る