[Flutter] FireAuth 로그인과 인증

김은상·2022년 10월 23일
0

Firebase에 Authentication을 이용해서 앱 안에서의 로그인과 사용자 인증을 다뤄보자.

두 가지 로그인을 구현해 볼까한다.

1. firebase_auth를 이용한 이메일 로그인과

2. firebase 소셜 로그인에 대해서 살펴보고 구현해보자.


1. email 로그인

콘솔 사이트에서 auth페이지에 이메일 로그인 사용을 설정해준다.

1) Firebase Auth 에 이메일 로그인 사용 설정
https://console.firebase.google.com/

2) firebase_auth 사용
패키지를 프로젝트에 추가하여 코드를 작성하면 바로 사용이 가능하다.
https://pub.dev/packages/firebase_auth

3) 코드 구현
https://firebase.flutter.dev/docs/auth/email-link-auth
구현은 위 docs링크를 참조해서 다양한 사용법을 읽어보시기 바랍니다.

import 'package:firebase_auth/firebase_auth.dart';
import 'main.dart';

class AuthManage{
  /// 회원가입
  Future<bool> createUser(String email, String pw) async{
    try {
      final credential = await FirebaseAuth.instance.createUserWithEmailAndPassword(
        email: email,
        password: pw,
      );
    } on FirebaseAuthException catch (e) {
      if (e.code == 'weak-password') {
        logger.w('The password provided is too weak.');
      } else if (e.code == 'email-already-in-use') {
        logger.w('The account already exists for that email.');
      }
    } catch (e) {
      logger.e(e);
      return false;
    }
    // authPersistence(); // 인증 영속
    return true;
  }

  /// 로그인
  Future<bool> signIn(String email, String pw) async{
    try {
      final credential = await FirebaseAuth.instance.signInWithEmailAndPassword(
          email: email,
          password: pw
      );
    } on FirebaseAuthException catch (e) {
      if (e.code == 'user-not-found') {
        logger.w('No user found for that email.');
      } else if (e.code == 'wrong-password') {
        logger.w('Wrong password provided for that user.');
      }
    } catch (e) {
      logger.e(e);
      return false;
    }
    // authPersistence(); // 인증 영속
    return true;
  }
  /// 로그아웃
  void signOut() async{
    await FirebaseAuth.instance.signOut();
  }

  /// 회원가입, 로그인시 사용자 영속
  void authPersistence() async{
    await FirebaseAuth.instance.setPersistence(Persistence.NONE);
  }
  /// 유저 삭제
  Future<void> deleteUser(String email) async{
    final user = FirebaseAuth.instance.currentUser;
    await user?.delete();
  }
  
  /// 현재 유저 정보 조회
  User? getUser(){
    final user = FirebaseAuth.instance.currentUser;
    if (user != null) {
      // Name, email address, and profile photo URL
      final name = user.displayName;
      final email = user.email;
      final photoUrl = user.photoURL;

      // Check if user's email is verified
      final emailVerified = user.emailVerified;

      // The user's ID, unique to the Firebase project. Do NOT use this value to
      // authenticate with your backend server, if you have one. Use
      // User.getIdToken() instead.
      final uid = user.uid;
    }
    return user;
  }
  /// 공급자로부터 유저 정보 조회
  User? getUserFromSocial(){
    final user = FirebaseAuth.instance.currentUser;
    if (user != null) {
      for (final providerProfile in user.providerData) {
        // ID of the provider (google.com, apple.cpm, etc.)
        final provider = providerProfile.providerId;

        // UID specific to the provider
        final uid = providerProfile.uid;

        // Name, email address, and profile photo URL
        final name = providerProfile.displayName;
        final emailAddress = providerProfile.email;
        final profilePhoto = providerProfile.photoURL;
      }
    }
    return user;
  }
  /// 유저 이름 업데이트
  Future<void> updateProfileName(String name) async{
    final user = FirebaseAuth.instance.currentUser;
    await user?.updateDisplayName(name);
  }
  /// 유저 url 업데이트
  Future<void> updateProfileUrl(String url) async{
    final user = FirebaseAuth.instance.currentUser;
    await user?.updatePhotoURL(url);
  }
  /// 비밀번호 초기화 메일보내기
  Future<void> sendPasswordResetEmail(String email) async{
    await FirebaseAuth.instance.setLanguageCode("kr");
    await FirebaseAuth.instance.sendPasswordResetEmail(email:email);
  }
}

다양한 메소드 중에서 맨위에 3가지 회원가입, 로그인, 로그아웃을 연결시켜 테스트 해보았다.

해당메서드에 email, pw를 넣어주고, bool값을 반환받도록 설정하여 테스트해보니 정상동작된다.

(UI 코드는 생략)

여기에 await중 처리 진행중이라는 사실을 알리기위한 progressBar

인증 에러 발생 시, 사유를 담아 SnackBar알림이나 AlertDialog 을 추가하면 더 좋을것 같다.


2. 소셜 로그인

파이어베이스에서는 다양한 소셜로그인을 사용할 수 있게 제공한다.

구글 로그인을 하나 붙여보겠다.

1) Firebase Auth 에 구글 로그인 사용 설정
https://console.firebase.google.com/

2) Firebase에 앱 서명을 추가
소셜로그인은 아래 설명과 같이 반드시 앱을 배포한 후에 sha인증서 지문이 있어야 사용이 가능하다.
https://firebase.google.com/docs/android/setup

SHA인증서 발급은 여기서 진행하면 된다.
https://developers.google.com/android/guides/client-auth
발급후 firebase에 등록해주었다면 이제 코드를 작성해보자.

3) google_sign_in 사용
프로젝트에 google_sign_in 을 install 해주자
https://pub.dev/packages/google_sign_in

4) 코드 구현
api 페이지에서 제공해주는 예시코드 본문 그대로이며,
이를 이해하여 쉽게 구현할 수 있다.

// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: public_member_api_docs

import 'dart:async';
import 'dart:convert' show json;

import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:http/http.dart' as http;

GoogleSignIn _googleSignIn = GoogleSignIn(
  // Optional clientId
  // clientId: '479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com',
  scopes: <String>[
    'email',
    'https://www.googleapis.com/auth/contacts.readonly',
  ],
);

void main() {
  runApp(
    const MaterialApp(
      title: 'Google Sign In',
      home: SignInDemo(),
    ),
  );
}

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

  
  State createState() => SignInDemoState();
}

class SignInDemoState extends State<SignInDemo> {
  GoogleSignInAccount? _currentUser;
  String _contactText = '';

  
  void initState() {
    super.initState();
    _googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount? account) {
      setState(() {
        _currentUser = account;
      });
      if (_currentUser != null) {
        _handleGetContact(_currentUser!);
      }
    });
    _googleSignIn.signInSilently();
  }

  Future<void> _handleGetContact(GoogleSignInAccount user) async {
    setState(() {
      _contactText = 'Loading contact info...';
    });
    final http.Response response = await http.get(
      Uri.parse('https://people.googleapis.com/v1/people/me/connections'
          '?requestMask.includeField=person.names'),
      headers: await user.authHeaders,
    );
    if (response.statusCode != 200) {
      setState(() {
        _contactText = 'People API gave a ${response.statusCode} '
            'response. Check logs for details.';
      });
      print('People API ${response.statusCode} response: ${response.body}');
      return;
    }
    final Map<String, dynamic> data =
        json.decode(response.body) as Map<String, dynamic>;
    final String? namedContact = _pickFirstNamedContact(data);
    setState(() {
      if (namedContact != null) {
        _contactText = 'I see you know $namedContact!';
      } else {
        _contactText = 'No contacts to display.';
      }
    });
  }

  String? _pickFirstNamedContact(Map<String, dynamic> data) {
    final List<dynamic>? connections = data['connections'] as List<dynamic>?;
    final Map<String, dynamic>? contact = connections?.firstWhere(
      (dynamic contact) => contact['names'] != null,
      orElse: () => null,
    ) as Map<String, dynamic>?;
    if (contact != null) {
      final Map<String, dynamic>? name = contact['names'].firstWhere(
        (dynamic name) => name['displayName'] != null,
        orElse: () => null,
      ) as Map<String, dynamic>?;
      if (name != null) {
        return name['displayName'] as String?;
      }
    }
    return null;
  }

  Future<void> _handleSignIn() async {
    try {
      await _googleSignIn.signIn();
    } catch (error) {
      print(error);
    }
  }

  Future<void> _handleSignOut() => _googleSignIn.disconnect();

  Widget _buildBody() {
    final GoogleSignInAccount? user = _currentUser;
    if (user != null) {
      return Column(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          ListTile(
            leading: GoogleUserCircleAvatar(
              identity: user,
            ),
            title: Text(user.displayName ?? ''),
            subtitle: Text(user.email),
          ),
          const Text('Signed in successfully.'),
          Text(_contactText),
          ElevatedButton(
            onPressed: _handleSignOut,
            child: const Text('SIGN OUT'),
          ),
          ElevatedButton(
            child: const Text('REFRESH'),
            onPressed: () => _handleGetContact(user),
          ),
        ],
      );
    } else {
      return Column(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          const Text('You are not currently signed in.'),
          ElevatedButton(
            onPressed: _handleSignIn,
            child: const Text('SIGN IN'),
          ),
        ],
      );
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Google Sign In'),
        ),
        body: ConstrainedBox(
          constraints: const BoxConstraints.expand(),
          child: _buildBody(),
        ));
  }
}

이걸 활용해서 아래와 같이

버튼을 추가해 로그인을 연결해주면 정상적으로 로그인됨을 확인할 수 있다.

Firebase Auth를 이용하여

이메일 로그인 및 구글 로그인을 공부하고 작성해보았다.

다른 로그인도 필요에 따라 이와 같은 순서로 쉽게 붙여 사용하고 커스텀할 수 있을것이다.

profile
Flutter 시작

0개의 댓글