오늘의 목표
앱 로그인시 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
- 저장 후 화면 하단의 콜백URL은 추후 애플 개발자 센터에 등록이 필요하니 메모해두자.
무슨 작업을 할 것인가?
애플 개발자 사이트에 가서 애플 로그인을 수행할 앱을 등록하고, 해당 앱에서 애플 로그인을 사용하도록 Key를 생성하고 Service ID를 생성해줄 것이다.
다음으로 Key 를 생성해볼 것이다.
이제 Services ID 를 생성해보자.
App ID 가 com.aaaaaa.bbbbb 였다면 Service ID 는 bbbbb.aaaaaa.com
#2. Firebase hosting 시작하기 > URL 기억해두기
애플 로그인을 사용하기 위해선 hosting이 필요한 것 같은데... 일단 참고해서 진행해두는 것이 좋을 것 같다. 아래의 링크로 진행 후 실제 구현 코드를 작성해 보도록 하자.
2021.03.09 - [트렌드/Flutter & Dart] - 매우 간단하게 Firebase Hosting 시작하기
Save 를 클릭했더니 아까 입력했던 Return URL에 문제가 있다는 팝업이 보인다. https:// 를 붙이라고 하니 다시 돌아가 붙여주자.
Signing 추가하는 방법은 아래 링크를 통해 확인하도록 하자.
위 캡쳐화면에서 + Capability 를 추가하여 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로 로그인' 또는 '애플로 로그인' 버튼 클릭 (최초 로그인시)
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 버튼 추가하기
댓글