ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [flutter + firebase auth] 플러터 + 파이어베이스 로그인 연동 <카카오 로그인> part-7
    개발일지/flutter 2021. 1. 23. 19:00

    안녕하세요 개발하는 남자 개남입니다.

    오늘은 파이어 베이스로 카카오 로그인을 연동하는 포스팅을 진행하겠습니다.

    혹시 Glitch 서버와 Firebase Admin이 세팅이 되시지 않았다면 이전 포스팅들을 보시면서 세팅 후에 돌아오시길 추천드립니다.

     

    [flutter + firebase auth] 플러터 + 파이어베이스 로그인 연동 part-6

    안녕하세요 개발하는 남자 개남입니다. 지난 포스팅에 이어서 본격적인 카카오, 네이버 로그인을 지원하기 위해 설정에 들어가 보겠습니다. 가장 먼저 firebase에 유저를 생성하기 위해 인증서버

    sudarlife.tistory.com


    카카오 개발자 콘솔 관리

    카카오 개발자 콘솔로 접속해서 카카오 계정으로 로그인해 줍니다.

     

    Kakao Developers

    카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

    developers.kakao.com

    상단 메뉴에서 내 애플리케이션 메뉴로 이동해줍니다.

     

    애플리케이션 추가하기 버튼을 클릭해서 지금 구성중인 앱 로그인을 위한 앱을 추가해줍니다.

    필수 항목인 앱 이름, 사업자명을 입력해주고 앱 아이콘도 있다면 등록해줍니다.

    모두 기입 후 저장 버튼을 클릭~!

     

    내 애플리케이션 리스트에 방금 추가한 앱을 선택해서 앱 관리로 들어가 줍니다.

    4가지의 키들이 보일 것입니다.

    그중에서 웹 로그인을 구현할 것이기 때문에 REST API 키만 필요합니다. ( 지금은 키 복사를 할 필요가 없기 때문에 잠시 뒤에 살펴보자 )

    앱 관리 중에 가장 먼저 할 것은 플랫폼 설정입니다.

    플렛폼 섹션의 플렛폼 설정하기 링크를 클릭해줍니다.

    웹 로그인을 할 거라서 안드로이드나 iOS를 설정할 필요가 없습니다. Web 플랫폼 등록 버튼 클릭

     

     

    API를 사용이 될 도메인을 등록해줍니다. 이곳에서 지난번에 인증 서버로 만들었던 Glitch 서버 도메인을 입력해줍니다.

    (실 서비스 구현 시에는 별도의 인증 서버를 두고 해당 도메인을 사용하기를 권장드립니다.)

     

    정상적으로 등록이 완료되었습니다.

    하단에 보시면 카카오 로그인 사용 시 Redirect URI를 등록해야 합니다.라고 나와있습니다.

    등록하러 가기 링크 클릭 ~!

    로그인 활성화를 설정해 줍니다. 그리고 하단에 Redirect URI 등록 버튼 클릭해서 로그인 완료 시 돌려줄 서버의 Return URI를 등록해 줍시다.

    아직 Glitch 서버에 받아줄 rest api를 등록해주지 않았기 때문에 잠시 멈추고 Glitch 서버로 이동해서 받아줄 api를 만들어 보겠습니다.

    app.get('/callbacks/kakao/sign_in', async (request, response) => {
      //Authentication Code 받아 돌려줄 api 
    })
    app.post('/callbacks/kakao/token', async (request, response) => {
      //발급 받은 kakao AccessCode로 사용자 확인후 firebase 로 custom token 생성하기 위한 api
    })

    구현은 이후에 해보도록 하고 받아줄 api만 확보하고 다시 카카오 콘솔로 넘어가서 Redirect URI 를 등록해 보겠습니다.

    Glitch에 만들어 놓은 api url path를 도메인화 함께 등록해서 저장해줍니다.

    자 이제 사용자의 데이터를 어디까지 불러올지 정하는 동의 항목를 선택해줘야 합니다.

     

    왼쪽 메뉴에서 동의항목 메뉴 클릭해줍니다.

     

     

    앱의 정책에 따라 가져올 데이터를 설정해줍니다. 저는 간단 로그인 테스트이기 때문에 이메일과 프로필 정보만 설정하도록 하겠습니다.

    다양한 동의 타입을 선택할 수 있습니다.

    예를 들어 앱에서 닉네임, 프로필 사진 , 이메일 등을 반드시 가져와야 한다면 필수 동의를 해야 합니다. 그리고 동의 목적을 채워 줘야 합니다. (단, 이메일 같은 경우 검수를 통과해야 필수 동의가 가능합니다.)

     

    위와 같이 설정했습니다.

    자 이제 카카오 개발자 콘솔에서 설정할 부분은 끝이 났습니다.

    이제 Glitch 인증 서버 작업을 진행해보겠습니다.


    Glitch 인증 서버(Node js)

    우선 Authorization_code 받아줄 api 먼저 작성하자면 아래 코드와 같이 하면 됩니다.

    app.get('/callbacks/kakao/sign_in', async (request, response) => {
      //Authentication Code 받아 돌려줄 api 
      const redirect = `webauthcallback://success?${new URLSearchParams(request.query).toString()}`;
      console.log(`Redirecting to ${redirect}`);
      response.redirect(307, redirect);
    })

    말 그대로 callbackUrlScheme을 통해 카카오 인증에서 보내준 Authorization_code를 앱으로 돌려주는 api 용도입니다.

    다음은 앱에서 발급받은 AccessToken을 전달받아 카카오 사용자 확인 후 Firebase Admin을 통해 token을 생성하는 api를 만들어 보겠습니다.

    app.post('/callbacks/kakao/token', async (request, response) => {
      //발급 받은 kakao AccessCode로 사용자 확인후 firebase 로 custom token 생성하기 위한 api
      kakao_auth.createFirebaseToken(request.body["accessToken"],(resulst)=>{
        response.send(resulst);
      });
    })

    이곳에서 kakao_auth 부분은 별도의 kakao_auth.js를 만들어서 상단에서 import를 시켜준 것입니다.

    const kakao_auth = require('./kakao_auth.js');

    kakao_auth.js 파일에서는 총 3가지의 일을 합니다. 

    첫 번째, accessToken으로 kapi.kakao.com/v2/user/me호출하여 정상적인 데이터를 불러올 수 있는지 확인

    (즉 변조된 토근인지 아닌지 확인하는 프로세스 진행 )

    두 번째, 받은 데이터를 토대로 Firebase Admin을 통해 Firebase에 사용자 생성 혹은 업데이트 

    세 번째, 생성된 사용자의 uid를 토대로 CustomToken 생성 

     

    카카오에서 파이어 베이스로 연동할 때 사용하라고 제공한 소스를 사용했습니다.

    github.com/FirebaseExtended/custom-auth-samples/tree/master/kakao

     

    FirebaseExtended/custom-auth-samples

    Samples showcasing how to sign in Firebase using additional Identity Providers - FirebaseExtended/custom-auth-samples

    github.com

    단 여기서 request이 deplicated 되어서 사용하지 못하기에 별도로 추가 수정을 통해서 구현하였습니다.

    풀 소스는 다음과 같습니다.

    'use strict'
    //https://github.com/FirebaseExtended/custom-auth-samples/tree/master/kakao 카카오에서 제공하는 본 코드입니다.
    const admin = require('./firebase_admin.js');
    const Async = require('async');
    const axios = require('axios');
    
    
    const kakaoRequestMeUrl = 'https://kapi.kakao.com/v2/user/me'
    
    /**
     * requestMe - Returns user profile from Kakao API
     *
     * @param  {String} kakaoAccessToken Access token retrieved by Kakao Login API
     * @return {Promiise<Response>}      User profile response in a promise
     */
    function requestMe(kakaoAccessToken,callback) {
      console.log('Requesting user profile from Kakao API server. '+ kakaoAccessToken)
      return axios.get(kakaoRequestMeUrl,{
        method: 'GET',
        headers: {'Authorization': 'Bearer ' + kakaoAccessToken}
      }).then((result)=>{
        callback(null,result.data,result);
      });
    }
    
    /**
       * updateOrCreateUser - Update Firebase user with the give email, create if
       * none exists.
       *
       * @param  {String} userId        user id per app
       * @param  {String} email         user's email address
       * @param  {String} displayName   user
       * @param  {String} photoURL      profile photo url
       * @return {Prommise<UserRecord>} Firebase user record in a promise
       */
    function updateOrCreateUser(userId, email, displayName, photoURL) {
      console.log('updating or creating a firebase user');
      const updateParams = {
        provider: 'KAKAO',
        displayName: displayName,
      };
      if (displayName) {
        updateParams['displayName'] = displayName;
      } else {
        updateParams['displayName'] = email;
      }
      if (photoURL) {
        updateParams['photoURL'] = photoURL;
      }
      console.log(updateParams);
      return admin.auth().updateUser(userId, updateParams).then(function(userRecord) {
        // See the UserRecord reference doc for the contents of `userRecord`.
        console.log("Successfully updated user", userRecord.toJSON());
        userRecord['uid'] = userId;
        if (email) {
          userRecord['email'] = email;
        }
        return admin.auth().createUser(userRecord);
      });
    }
    
    /**
     * createFirebaseToken - returns Firebase token using Firebase Admin SDK
     *
     * @param  {String} kakaoAccessToken access token from Kakao Login API
     * @return {Promise<String>}                  Firebase token in a promise
     */
    function createFirebaseToken(kakaoAccessToken,callback) {
    
      Async.waterfall([
        (next)=>{
          requestMe(kakaoAccessToken,(error,response,boy)=>{
            console.log(response)
            const body =response // JSON.parse(response)
            console.log(body)
            const userId = `kakao:${body.id}`
            if (!userId) {
              return response.status(404)
              .send({message: 'There was no user with the given access token.'})
            }
            let nickname = null
            let profileImage = null
            if (body.properties) {
              nickname = body.properties.nickname
              profileImage = body.properties.profile_image
            }
    
            const updateParams = {
              uid :userId,
              email :body.kakao_account.email,
              provider: 'KAKAO',
              displayName: nickname,
            };
            if (nickname) {
              updateParams['displayName'] = nickname;
            } else {
              updateParams['displayName'] = body.kakao_account.email;
            }
            if (profileImage) {
              updateParams['photoURL'] = profileImage;
            }
    
            next(null,updateParams)
          });
        },
        (userRecord, next) => {
          console.log(userRecord.email);
          admin.auth().getUserByEmail(userRecord.email).then((userRecord)=>{
            next(null,userRecord);
          }).catch((error)=>{
            console.log(error);
            admin.auth().createUser(userRecord).then((user)=>{
              next(null,user)
            })
          })
        },
        (userRecord, next) => {
          console.log(userRecord);
          console.log("**************");
          const userId = userRecord.uid
          console.log(`creating a custom firebase token based on uid ${userId}`)
          admin.auth().createCustomToken(userId, {provider: 'KAKAO'}).then((result)=>{
            console.log(result);  
            next(null , result);
          });
        }
      ],(err, results) => {
          console.log(results)
          callback(results);
      });
    
    }
    
    module.exports={
      createFirebaseToken
    }

    자 이제 모든 설정은 끝이 났습니다.

    이제 클라이언트 앱에서 작업하러 프로젝트 소스로 이동해보겠습니다.


    프로젝트 소스 관리

    기존 소스에 이어서 작업하기 때문에 별도의 프로젝트 구성에 대해서 설명은 생략하도록 하겠습니다.

    (혹시 이 포스팅부터 보신다면 part 1부터 보시는 것을 추천드립니다.)

     

    iOS는 별도의 세팅 없이도 바로 로그인이 가능하지만 

    Android는 AndroidManifast.xml 파일에 설정할 것이 하나 있습니다.

    그것은 CallbackActivity설정입니다.

    Glich 서버에 callback으로 사용했던 callbackUrlScheme를 등록해 주면 됩니다.

    <activity android:name="com.linusu.flutter_web_auth.CallbackActivity" >
      <intent-filter android:label="flutter_web_auth">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="<이곳에 callbackUrlScheme 넣어줍니다.>" />
      </intent-filter>
    </activity>

    이제 다음으로 소스코드만 추가하면 됩니다.

    login.dart. 파일에 카카오 로그인 버튼을 추가해주겠습니다.

    FlatButton(
      color: Colors.grey.withOpacity(0.3),
      onPressed: signInWithKaKao,
      child: Text("KaKao Login"),
    ),

    onPressed 클릭 시 로그인 프로세스를 진행할 signInWithKaKao 로직을 아래와 같이 작성합니다.

     

    Future<UserCredential> signInWithKaKao() async {
        final clientState = Uuid().v4();
        final url = Uri.https('kauth.kakao.com', '/oauth/authorize', {
          'response_type': 'code',
          'client_id': "<카카오 관리 콘솔에서 제공하는 REST_API 키 입력>",
          'response_mode': 'form_post',
          'redirect_uri':
              '<카카오에 등록한 authrization_code 받을 return uri 입력>',
          'scope': 'account_email profile',
          'state': clientState,
        });
    
        final result = await FlutterWebAuth.authenticate(
            url: url.toString(),
            callbackUrlScheme: "webauthcallback"); //"applink"//"signinwithapple"
        final body = Uri.parse(result).queryParameters;
        print(body["code"]);
    
        final tokenUrl = Uri.https('kauth.kakao.com', '/oauth/token', {
          'grant_type': 'authorization_code',
          'client_id': "<카카오 관리 콘솔에서 제공하는 REST_API 키 입력>",
          'redirect_uri':
              '<카카오에 등록한 authrization_code 받을 return uri 입력>',
          'code': body["code"],
        });
        var responseTokens = await http.post(tokenUrl.toString());
        Map<String, dynamic> bodys = json.decode(responseTokens.body);
        var response = await http.post(
            "https://sage-dorian-anise.glitch.me/callbacks/kakao/token",
            body: {"accessToken": bodys['access_token']});
        return FirebaseAuth.instance.signInWithCustomToken(response.body);
      }

    이제 안드로이드 / iOS 로그인 시도해보시면 로그인이 정상적으로 진행되실 것입니다.

    자 이제 마지막 Naver 로그인만 남았습니다.

    오늘은 여기까지~! 

    댓글

Designed by Tistory.