본문 바로가기
프로그래밍/Flutter & Dart

Flutter iOS 애플 로그인 기능(Sign in with apple) 구현하기

by 어느덧중반 2021. 3. 9.
반응형

 

오늘의 목표

앱 로그인시 Apple 로그인 기능 구현하기

#1. Firebase Authentication > Apple sign in 활성화하기

#2. Firebase hosting 시작하기 > URL 기억해두기

#3. Flutter 에서 Apple with sign in 코드 작성하기 (낱낱이 파헤쳐보자)

 


iOS앱은 로그인화면에 구글/페이스북/트위터 등 다른 SNS로그인 버튼이 있는데 애플로그인을 추가하지 않으면 앱스토어에 앱을 등록할 수 없다는 정책이 있기 때문에 이번 기회를 계기로 애플 로그인 기능을 추가해보고자 한다. 차근차근 하나하나 따라해보자.

 

#1. Firebase Authentication > Apple sign in 활성화하기

Firebase의 Authentication 플랫폼은 로그인을 하기 위한 인증 서비스이다. 이 곳에 Apple 로그인 기능을 추가해야 한다.

 - Firebase > 내 프로젝트 (프로젝트 생성이 안되어 있다면 생성 및 iOS앱부터 추가해주자.) > Authentication > Sign-in method

하단의 Apple을 사용 설정됨으로 변경시켜주자.

 - 저장 후 화면 하단의 콜백URL은 추후 애플 개발자 센터에 등록이 필요하니 메모해두자.

 

무슨 작업을 할 것인가?

애플 개발자 사이트에 가서 애플 로그인을 수행할 앱을 등록하고, 해당 앱에서 애플 로그인을 사용하도록 Key를 생성하고 Service ID를 생성해줄 것이다.

애플 개발 사이트 접속 > 로그인 후 Account 페이지로 이동
Certificates, IDs & Profiles 메뉴로 이동
Identifiers 로 이동하여 + 버튼으로 App ID를 생성하자.
우리는 App ID가 필요하다. 선택 후 Continue
App 선택 후 Continue
Description 에는 앱 설명, Bundle ID는 XCode에서 확인되는 ID 작성하자.
Bundle ID는 XCode에서 위 경로에서 확인 가능 (Runner > General > Bundle Identifier)
Description, Bundle ID 입력하고 Sign in with Apple 을 찾아 아래로 스크롤
Sign in with Apple 체크 후 Continue 누르면 Register 버튼으로 변경될 것이다. Register 버튼 클릭해서 등록절차를 마치자.

 

다음으로 Key 를 생성해볼 것이다.

Key 메뉴로 이동하고 + 버튼으로 새로운 Key 를 등록해보자.
Key Name을 입력하고 Sign in with Apple 을 체크 하자
Sign in with Apple 우측에 Configure 클릭
아까 만들어둔 App ID 를 체크하면 Save 버튼이 활성화될 것이다.
Key 등록 화면으로 돌아올 것이다. 이제 Continue 버튼을 클릭해 등록을 마저 진행하자.
이제 마지막이다. Register 버튼으로 등록하자.
Download 버튼을 클릭해 생성한 Key 를 다운받자. 1회만 다운이 가능하므로 다운로드 후 별도의 폴더에 잘 보관해두자.

이제 Services ID 를 생성해보자.

Identifier 메뉴로 이동해 + 버튼을 클릭하자.
Services IDs 선택 후 Continue 클릭
Description 과 Identifier 를 입력해주자. Identifier의 경우 App ID의 순서와 반대로 적어주고 Continue 클릭 

App ID 가 com.aaaaaa.bbbbb 였다면 Service ID 는 bbbbb.aaaaaa.com

Register 클릭
생성된 Service ID 를 클릭해보자.
Sign in with Apple 을 사용할 수 있어 보인다. 
체크하고 Configure 를 클릭하자.

 

#2. Firebase hosting 시작하기 > URL 기억해두기

애플 로그인을 사용하기 위해선 hosting이 필요한 것 같은데... 일단 참고해서 진행해두는 것이 좋을 것 같다. 아래의 링크로 진행 후 실제 구현 코드를 작성해 보도록 하자.

2021.03.09 - [트렌드/Flutter & Dart] - 매우 간단하게 Firebase Hosting 시작하기

 

 

매우 간단하게 Firebase Hosting 시작하기

Firebase를 이용하면 무료로 Hosting 이 가능하다. 본인의 웹페이지를 만들고 싶거나, 개인정보 처리방침 등의 static한 페이지를 만들거나, 앱에서 애플 로그인을 구현할 때 등 많은 상황에서 호스팅

kyungsnim.net

 

App ID를 선택해주고 Domain / Return URL 을 입력하자.
잘 입력했는지 확인하고 Done 을 클릭하자.
다시 Service ID Confituration 메뉴로 돌아오게 되면 Continue 를 클릭하자.
마지막으로 Save 를 클릭해주자.

Save 를 클릭했더니 아까 입력했던 Return URL에 문제가 있다는 팝업이 보인다. https:// 를 붙이라고 하니 다시 돌아가 붙여주자.

위에는 https:// 가 안붙고 아래 Return URLs 쪽에만 https:// 를 붙여주자.
다시 Done
다시 Save
정상적으로 등록되었다.

 

 

아이폰 실 기기에서 sign in with apple 테스트가 가능하므로 Signing & Capabilities에서 Team을 추가하자

Signing 추가하는 방법은 아래 링크를 통해 확인하도록 하자.

위 캡쳐화면에서 + Capability 를 추가하여 Sign in with Apple 을 추가해주자

Sign in with Apple 더블클릭
Sign in with Apple 추가된 것을 확인할 수 있다.

 

#3. Flutter 에서 Apple with sign in 코드 작성하기 (낱낱이 파헤쳐보자)

 - Apple 로그인의 과정은 아래와 같다. 부디 아래의 내용을 잘 읽어보고 애플 로그인에 대해 완벽하게 숙지하길 바란다.

  • apple_sign_in, firebase_auth, flutter_secure_storage 패키지 pubspec.yaml에 추가하기 (버전은 각자 맞추도록 하자.)
    각각 뭐하는 역할을 하는지는 아래와 같다.

    - apple_sign_in : 실제로 애플 로그인이 동작하기 위해 필요한 패키지
    - firebase_auth : 애플 로그인시 idToken을 이용하여 firebase 의 authentication 인증에 사용되는 패키지 (선택사항)
    - flutter_secure_storage : 최초 애플 로그인 이후 자동로그인 설정을 하기 위한 패키지

apple_sign_in, firebase_auth, flutter_secure_storage 총 3가지를 추가해주었다.

  • 'Apple로 로그인' 또는 '애플로 로그인' 버튼 클릭 (최초 로그인시)

나는 대략... 이런 식으로 만들어 두었다.

InkWell(
  onTap: () => appleLogIn(),
  child: loginButton(
    context,
    'assets/images/apple_icon.png',
    'Sign in with Apple',
    Colors.white,
    Colors.black,
    Colors.black12)
)
  • 애플 로그인이 이용 가능한지 체크
  • 로그인 동작 수행 (Face ID 또는 Password 입력)
  • 로그인 권한여부 체크
    (실패하는 경우는 비밀번호를 잘못입력했더나, 로그인화면을 취소했거나, 애플로그인 기능을 사용할 수 없는 기기일 때 발생함)
  • 애플 idToken을 이용하여 Firebase Authentication 인증 수행 (Firebase Authentication 사용안하는 경우 건너뛰어도 됨)
  • Firebase Auth 수행 후 수행결과값으로 회원가입 정보 컨트롤
  // for apple login
  void appleLogIn() async {
    // Firebase authentication 추가 인증작업용
    final _firebaseAuth = FirebaseAuth.instance;
    List<Scope> scopes = [Scope.email, Scope.fullName];

    // 애플 로그인이 이용 가능한지 체크
    if (await AppleSignIn.isAvailable()) {
      // 로그인 동작 수행 (Face ID 또는 Password 입력)
      final AuthorizationResult result = await AppleSignIn.performRequests([
        AppleIdRequest(requestedScopes: [Scope.email, Scope.fullName])
      ]);

      // 로그인 권한여부 체크
      switch (result.status) {
        // 로그인 권한을 부여받은 경우
        case AuthorizationStatus.authorized:
          // Store user ID (자동로그인을 위한 인증된 user정보 저장)
          await FlutterSecureStorage()
              .write(key: "userId", value: result.credential.user);
              
          // 애플 로그인 인증 후 결과값으로 Firebase authentication 데이터 넣는 작업
          final appleIdCredential = result.credential;
          final oAuthProvider = OAuthProvider('apple.com');
          final credential = oAuthProvider.credential(
            idToken: String.fromCharCodes(appleIdCredential.identityToken),
            accessToken: String.fromCharCodes(appleIdCredential.authorizationCode),
          );
          // firebase auth로 인증절차 (firebase auth를 사용안할 경우 아래 작업은 안해도 된다.)
          // credential 안에 애플 정보는 담겨 있다. (email, fullName 등)
          final authResult = await _firebaseAuth.signInWithCredential(credential);

          // 인증 완료되면 firebaseUser 값으로 반환
          final firebaseUser = authResult.user;

          // 애플의 fullName이 있다면 구글용 displayName으로 변환 해서 profile 업데이트 해주기
          if(scopes.contains(Scope.fullName)) {
            final displayName = '${appleIdCredential.fullName.givenName} ${appleIdCredential.fullName.familyName}';
            await firebaseUser.updateProfile(displayName: displayName);
          }

          // login 정보로 컨트롤 해보자.
          saveAppleUserInfoToFirestore(firebaseUser);
          break;

        case AuthorizationStatus.error:
          print("Sign in failed: ${result.error.localizedDescription}");
          setState(() {
            errorMessage = "Sign in failed 😿";
          });
          break;

        case AuthorizationStatus.cancelled:
          print('User cancelled');
          break;
      }
    } else {
      print('Apple SignIn is not available for your device.');
    }
  • (성공시) 기기에 로그인 정보 기억해두기 (자동로그인을 위해 하는 과정)
  • 사용자 정보로 회원가입 시켜주고 로그인 후 다음화면으로 진입 (회원가입 시켜주는건 곧 DB에 저장하는 과정)

  // for apple login
  saveAppleUserInfoToFirestore(User user) async {
    // 해당 유저의 db정보 가져오기
    DocumentSnapshot documentSnapshot = await userReference.doc(user.uid).get();

    // 해당 유저의 db정보가 없다면
    if (!documentSnapshot.exists) {

      // 유저정보 셋팅된 값으로 db에 set
      userReference.doc(user.uid).set({
        'id': user.uid,
        'profileName': user.displayName != null ? user.displayName : "",
        'email': user.email,
        'createdAt': DateTime.now(),
      });
    } 
    
    // 기기에 로그인 정보 기억해두기 (자동로그인을 위해 하는 과정)
    await FlutterSecureStorage().write(key: "appleUserUid", value: user.uid);
    
    // 현재 유저정보에 값 셋팅하기
    setState(() {
      currentUser = CurrentUser.fromDocument(documentSnapshot);
    });

	// 로그인 이후 화면으로 진입
    Navigator.pushReplacement(
        context, MaterialPageRoute(builder: (context) => MainPage(0)));
  }
  • (이후에 로그인시) 기억하고 있는 로그인정보 불러오기
  • 로그인정보가 있다면 해당 사용자를 맵핑시켜주고 로그인처리

    아래의 소스는 Login 페이지에 해당한다. (로그인 페이지 진입시 자동로그인 대상인지 체크 후 맞으면 자동로그인 수행)
  @override
  initState() {
    super.initState();

    // 앱 실행시 애플 사용자의 변경여부를 확인함
    checkLoggedInState();
    AppleSignIn.onCredentialRevoked.listen((_) {
      print("Credentials revoked");
    });
  }
  
  // for apple login
  void checkLoggedInState() async {
    // 자동로그인 정보 불러오기
    final userId = await FlutterSecureStorage().read(key: "userId");
    final appleUserUid = await FlutterSecureStorage().read(key: "appleUserUid");
    if (userId == null) {
      print("No stored user ID");
      return;
    }

    final credentialState = await AppleSignIn.getCredentialState(userId);
    switch (credentialState.status) {
      // 자동 로그인
      case CredentialStatus.authorized:

        if (appleUserUid != null && appleUserUid != "") {
          // 해당 정보로 사용자 정보 가져오기
          DocumentSnapshot documentSnapshot =
              await userReference.doc(appleUserUid).get();

          // 현재 유저정보에 값 셋팅하기
          setState(() {
            currentUser = CurrentUser.fromDocument(documentSnapshot);
          });

		  // 로그인 이후 화면 진입
          Navigator.pushReplacement(
              context, MaterialPageRoute(builder: (context) => MainPage(0)));
        }
        break;
      case CredentialStatus.error:
        print(
            "getCredentialState returned an error: ${credentialState.error.localizedDescription}");
        break;

      case CredentialStatus.revoked:
        print("getCredentialState returned revoked");
        break;

      case CredentialStatus.notFound:
        print("getCredentialState returned not found");
        break;

      case CredentialStatus.transferred:
        print("getCredentialState returned not transferred");
        break;
    }
  }
  • 로그아웃 버튼 기능 마지막으로 추가하기
  logoutUser() async {
    try {
      // 자동로그인을 위한 사용자 정보 삭제
      await FlutterSecureStorage().deleteAll();
      
      // 로그인 화면으로 이동
      Navigator.pushReplacement(
            context, MaterialPageRoute(builder: (context) => LoginPage()));
    } catch (e) {
      print(e);
    }
  }

 

이 것으로 Flutter 에서 구현하는 애플 로그인 편을 마치겠다. 혹시 카카오톡 로그인도 구현하고자 한다면 아래의 링크로 이동해 참고하도록 하자.

2020.07.30 - [트렌드/Flutter & Dart] - 5분만에 Flutter 에서 kakao login 버튼 추가하기

 

5분만에 Flutter 에서 kakao login 버튼 추가하기

Flutter에서 소셜 로그인을 할 수 있는 여러 가지 방법이 있는데 그 중 하나가 카카오계정 로그인이다. Flutter에서 간단히 구현을 해볼텐데 방법은 아래와 같다. 1. pub.dev (flutter 패키지 모음) 에서

kyungsnim.net



반응형

댓글