SupabaseでLogin
status code 400 error
よくわからないエラーにハマった 😱
今回は前回やってみたSupabaseのチュートリアルを参考に認証機能の実装をやってみました
メールアドレスのログインは、最初から設定されているので、特に設定がいらないと思ったのですが、2箇所だけスイッチをオフにしないとログインだけできませんでした!
参考にした資料
解決方法
認証の設定をこのようにしたら、ユーザーは作れるけど、ログインできない問題を解決できた!
作成したアプリ
ユーザー作成、ログイン、ログアウトするだけのシンプルなものになっております。URLとanon keyは環境変数で隠していますが、ローカル環境で動かすだけなら、そのままmain関数に貼り付けて使ってもらっても大丈夫です。
前回作成した記事やドキュメントを参考に設定しみてください。
認証機能をまとめたクラス。ここから、ロジックを呼び出す。
最初は、プログラムをメソッドにせずに、ボタンの中にベタ書きしてました😅
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()));
}
}
ユーザーの新規作成
ユーザーの新規作成はこちらのページで行います。
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と表示されるページしかありませんが入門レベルには最適かなと思います。
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'))
],
),
),
);
}
}
アプリを実行するコード
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で使ってみましたが、認証機能が作らなくても最初から用意されているので、気軽に導入できました。
とはいってもセキュリティーの設定だったり難しい課題は残っていますので、まだまだ勉強が必要です。
Discussion