🔑

SupabaseでLogin

2023/02/20に公開

status code 400 error

よくわからないエラーにハマった 😱

今回は前回やってみたSupabaseのチュートリアルを参考に認証機能の実装をやってみました
メールアドレスのログインは、最初から設定されているので、特に設定がいらないと思ったのですが、2箇所だけスイッチをオフにしないとログインだけできませんでした!

参考にした資料

https://supabase.com/docs/reference/dart/installing
https://supabase.com/docs/guides/auth/auth-email

解決方法

認証の設定をこのようにしたら、ユーザーは作れるけど、ログインできない問題を解決できた!

作成したアプリ

ユーザー作成、ログイン、ログアウトするだけのシンプルなものになっております。URLとanon keyは環境変数で隠していますが、ローカル環境で動かすだけなら、そのままmain関数に貼り付けて使ってもらっても大丈夫です。
前回作成した記事やドキュメントを参考に設定しみてください。

https://zenn.dev/flutteruniv_dev/articles/da265483f0073c

認証機能をまとめたクラス。ここから、ロジックを呼び出す。
最初は、プログラムをメソッドにせずに、ボタンの中にベタ書きしてました😅

model/auth_model.dart
import 'package:flutter/material.dart';
import 'package:supabase_app/ui/signin_page.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

// 認証機能をまとめたクラス.
class AuthModel {
  // ゲッターを作成して、インスタンス化したSupabaseClientを渡す.
  SupabaseClient get supabase => Supabase.instance.client;

  // ユーザーの新規登録をするメソッド.
  Future<void> signUp(
      String _email, String _password, BuildContext context) async {
    try {
      final AuthResponse res = await supabase.auth.signUp(
        email: _email,
        password: _password,
      );
      final Session? session = res.session;
      final User? user = res.user;
    } catch (e) {
      print('error!: $e');
    }
  }
  // ログインをするメソッド.
  Future<void> signInWithEmail(
      String _email, String _password, BuildContext context) async {
    try {
      final AuthResponse res = await supabase.auth.signInWithPassword(
        email: _email,
        password: _password,
      );
      final Session? session = res.session;
      final User? user = res.user;
      Navigator.of(context)
          .pushReplacement(MaterialPageRoute(builder: (context) => HomePage()));
    } catch (e) {
      print('error!: $e');
    }
  }
  // ログアウトするメソッド.
  Future<void> signOut(BuildContext context) async {
    await supabase.auth.signOut();
    Navigator.of(context)
          .pushReplacement(MaterialPageRoute(builder: (context) => SigninPage()));
  }
}

ユーザーの新規作成

ユーザーの新規作成はこちらのページで行います。

ui/signup_page.dart
import 'package:flutter/material.dart';
import 'package:supabase_app/model/auth_model.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

class SignupPage extends StatefulWidget {
  SignupPage({Key? key}) : super(key: key);

  
  State<SignupPage> createState() => _SignupPageState();
}

class _SignupPageState extends State<SignupPage> {
  TextEditingController _email = TextEditingController();
  TextEditingController _password = TextEditingController();

  
  Widget build(BuildContext context) {
    // final supabase = Supabase.instance.client;
    final authModel = AuthModel();

    return Scaffold(
      appBar: AppBar(
        title: Text('SignUp'),
      ),
      body: Center(
        child: Column(
          children: [
            TextField(
              controller: _email,
            ),
            TextField(
              controller: _password,
            ),
            ElevatedButton(
                onPressed: () async {
                  // final AuthResponse res = await supabase.auth.signUp(
                  //   email: _email.text,
                  //   password: _password.text,
                  // );
                  // final Session? session = res.session;
                  // final User? user = res.user;
                  authModel.signUp(_email.text, _password.text, context);
                },
                child: Text('SignUp'))
          ],
        ),
      ),
    );
  }
}

ユーザーログインページ

ユーザー登録ができていたら、こちらのページからログインできます。 
ログインした後、Welcomeと表示されるページしかありませんが入門レベルには最適かなと思います。

ui/signup_page.dart
import 'package:flutter/material.dart';
import 'package:supabase_app/model/auth_model.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

class SignupPage extends StatefulWidget {
  SignupPage({Key? key}) : super(key: key);

  
  State<SignupPage> createState() => _SignupPageState();
}

class _SignupPageState extends State<SignupPage> {
  TextEditingController _email = TextEditingController();
  TextEditingController _password = TextEditingController();

  
  Widget build(BuildContext context) {
    // final supabase = Supabase.instance.client;
    final authModel = AuthModel();

    return Scaffold(
      appBar: AppBar(
        title: Text('SignUp'),
      ),
      body: Center(
        child: Column(
          children: [
            TextField(
              controller: _email,
            ),
            TextField(
              controller: _password,
            ),
            ElevatedButton(
                onPressed: () async {
                  // final AuthResponse res = await supabase.auth.signUp(
                  //   email: _email.text,
                  //   password: _password.text,
                  // );
                  // final Session? session = res.session;
                  // final User? user = res.user;
                  authModel.signUp(_email.text, _password.text, context);
                },
                child: Text('SignUp'))
          ],
        ),
      ),
    );
  }
}

アプリを実行するコード

main.dart
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:supabase_app/model/node_model.dart';
import 'package:supabase_app/ui/signin_page.dart';
import 'package:supabase_app/ui/signup_page.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // .envを読み込めるように設定.
  await dotenv.load(fileName: '.env');
  await Supabase.initialize(
    url: dotenv.get('VAR_URL'), // .envのURLを取得.
    anonKey: dotenv.get('VAR_ANONKEY'), // .envのanonキーを取得.
  );
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: SigninPage(),
    );
  }
}

アプリの動作確認


ユーザーの新規登録に成功するとログにいっぱい文字が出てくる 。
SignUp log

flutter: {"currentSession":{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjc2ODI2NzUwLCJzdWIiOiI4ZjYwZTIwOC01M2Q3LTRkZjctYTRiOS01M2YwZjlmNTliMDQiLCJlbWFpbCI6ImhvZ2VAY28uanAiLCJwaG9uZSI6IiIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIiwicHJvdmlkZXJzIjpbImVtYWlsIl19LCJ1c2VyX21ldGFkYXRhIjp7fSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTY3NjgyMzE1MH1dLCJzZXNzaW9uX2lkIjoiNGY2M2IzNDQtY2NkYi00ZGQ0LTgxYmYtMzU4Y2ViZjZiMjJkIn0.jsWgPcSBMrMJZopKNB1vTjsmJ1Eyb9xqNFz16jg9Mf8","expires_in":3600,"refresh_token":"eh9zCrdZd6OeOu2mG1mUMw","token_type":"bearer","provider_token":null,"provider_refresh_token":null,"user":{"id":"8f60e208-53d7-4df7-a4b9-53f0f9f59b04","app_metadata":{"provider":"email","providers":["email"]},"user_metadata":{},"aud":"authenticated","email":"hoge@co.jp","phone":"","created_at":"2023-02-19T16:12:30.937213Z","confirmed_at":null,"email_confirmed_at":"2023-02-19T16:12:30.94190598Z","ph<…>

ユーザーが登録されているかはこちらの画面で確認できます。

ログインに成功したときも暗号のような文字が出てくる。
SignIn log

flutter: **** onAuthStateChange: AuthChangeEvent.signedIn
flutter: {"currentSession":{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjc2ODI2ODc1LCJzdWIiOiI4ZjYwZTIwOC01M2Q3LTRkZjctYTRiOS01M2YwZjlmNTliMDQiLCJlbWFpbCI6ImhvZ2VAY28uanAiLCJwaG9uZSI6IiIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIiwicHJvdmlkZXJzIjpbImVtYWlsIl19LCJ1c2VyX21ldGFkYXRhIjp7fSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTY3NjgyMzI3NX1dLCJzZXNzaW9uX2lkIjoiMDBiNjA2MjktZGYxNy00NzA3LThlYjEtNGI3Njg4Zjc0OWZhIn0.TM22w_iVLynU0zWoYbLeIxp5CbEATrQe6FwMogYPKzg","expires_in":3600,"refresh_token":"iPXun0246sJKPgioPBPP7Q","token_type":"bearer","provider_token":null,"provider_refresh_token":null,"user":{"id":"8f60e208-53d7-4df7-a4b9-53f0f9f59b04","app_metadata":{"provider":"email","providers":["email"]},"user_metadata":{},"aud":"authenticated","email":"hoge@co.jp","phone":"","created_at":"2023-02-19T16:12:30.937213Z","confirmed_at":"2023-02-19T16:12:30.941905Z","email_confirmed_at":"2023-02-1<…>

ログアウトするときもこのようなログが出てくる 
SignOut log

Reloaded 4 of 1202 libraries in 399ms (compile: 52 ms, reload: 228 ms, reassemble: 104 ms).
flutter: **** onAuthStateChange: AuthChangeEvent.signedOut

最後に

以前からReactで、使ってみたことのあるSupabaseをFlutterで使ってみましたが、認証機能が作らなくても最初から用意されているので、気軽に導入できました。
とはいってもセキュリティーの設定だったり難しい課題は残っていますので、まだまだ勉強が必要です。

Flutter大学

Discussion