카카오로그인 관련하여 질문드립니다

문의 시 사용하시는 SDK 버전 정보와 플랫폼(Android / iOS) 및 디벨로퍼스 앱ID를 알려주세요.
Flutter : 3.22.3 Android 앱 ID : 1122049


플러터로 카카오 로그인을 구현 중 입니다. ( 앱 서비스를 만드는 중 ) 처음에 안드로이드와 ios 설정 후, 실행했을 때는 크롬외에는 다 오류 없이 로그인이 진행되었습니다. 그래서 redirect url을 등록한 후, 크롬으로 로그인을 성공했는데, 인가 코드와 이런 것을 받기위해 카카오 공식문서에 있는것처럼 2단계로 나눠서 에뮬레이터로 크롬을 사용해서 로그인을 진행했는데, 이때는 카카오 문서에 적혀져있는 웹 처럼 2단계로 나눠서 하지 않아도 되는건가요? (카카오 공식문서에 적혀져있는것처럼 인가코드를 2단계로 나눠서 인가 코드와 토큰을 받는 것처럼 코드를 진행해야하는건가요,?)

그리고 추가적으로


이런식으로 안드로이드에서만 로그인 완료후 화면으로 돌아가지 않고 continue 버튼을 눌러도 제자리인 상태입니다…

관련 코드 블럭 기재 부탁드려요.

카카오 로그인 받아오는 관련 플러터 코드입니다

import 'package:flick_frontend/user/social_login.dart';
import 'package:kakao_flutter_sdk_user/kakao_flutter_sdk_user.dart';

class KakaoLogin implements SocialLogin {
  // To do : 웹 앱과 네이티브 앱을 차이두게 해서 구현 완료 해야 함.
  @override
  Future<bool> login() async {
    try {
      bool isInstalled = await isKakaoTalkInstalled();
      // 네이티브 앱이면서 카카오톡이 설치 된 경우 or 모바일 기기 웹 브라우저인 경우
      if (isInstalled) {
        // app 서비스로 구현 -> 앱 에서 받을 수 있는 인증토큰
        try {
          OAuthToken token = await UserApi.instance
              .loginWithKakaoTalk(); // 여기에서 동의하고 계속하기를 선택하면 인가코드가 발급되고, flutter sdk가 인가 코드로 토큰을 발급받아 로그인 완료함 (OpenID Connect를 사용하는 경우, ID 토큰을 함께 발급 받음)
          print(
              '카카오톡으로 로그인 성공1: ${token.accessToken}'); // -> 지금은 안 되는데 그 이유는 아마 상태관리가 안되어서 아닐까 싶다
          return true;
        } catch (e) {
          print('카카오톡으로 로그인 실패 1$e');
          return false;
        }
      }
      // 모바일 기기가 아니거나  or 네이티브 앱에서 했으나 카카오톡이 설치되어있지 않을 때 -> 에뮬도 여기에 속함 !
      else {
        try {
          OAuthToken token = await UserApi.instance.loginWithKakaoAccount();
          print(
              '카카오톡으로 로그인 성공2: ${token.accessToken}'); // -> 지금은 안 되는데 그 이유는 아마 상태관리가 안되어서 아닐까 싶다

          return true;
        } catch (e) {
          return false;
        }
      }
    } catch (error) {
      print('카카오톡으로 로그인 실패2 $error');

      return false;
    }
  }

  @override
  Future<bool> logout() async {
    try {
      await UserApi.instance.unlink();
      return true;
    } catch (error) {
      return false;
    }
  }
}

----- 카카오 로그인 ui 부분 화면입니다.

import 'package:flick_frontend/user/kakao_login.dart';
import 'package:flick_frontend/user/view_model/main_view.dart';
import 'package:flutter/material.dart';

class KakaoLoginScreen extends StatefulWidget {
  const KakaoLoginScreen({super.key, required this.title});

  final String title;

  @override
  State<KakaoLoginScreen> createState() => _KakaoLoginScreenState();
}

class _KakaoLoginScreenState extends State<KakaoLoginScreen> {
  final viewModel = MainViewModel(KakaoLogin());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Image.network(
                viewModel.user?.kakaoAccount?.profile?.profileImageUrl ?? ''),
            Text(viewModel.user?.kakaoAccount?.profile?.nickname ?? ''),
            Text(
              "${viewModel.isLogined}",
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            ElevatedButton(
                onPressed: () async {
                  await viewModel.login();
                  setState(() {});
                },
                child: const Text("Login")),
            ElevatedButton(
                onPressed: () async {
                  await viewModel.logout();
                  setState(() {});
                },
                child: const Text("Logout")),
          ],
        ),
      ),
    );
  }
}

— 안드로이드 매니패스트 .xml 부분입니다 → envied 패키지 이용해서 앱키와 등등 부분은 가렸습니다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:label="flick_frontend"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <activity 
        android:name="com.kakao.sdk.flutter.AuthCodeCustomTabsActivity"
        android:exported="true">
        <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" />
    
            <!-- Redirect URI, "kakao${YOUR_NATIVE_APP_KEY}://oauth" 형식 -->
            <data android:scheme="@string/kakao_native_app_key" android:host="oauth"/>
        </intent-filter>
    </activity>
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:taskAffinity=""
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
    <!-- Required to query activities that can process text, see:
         https://developer.android.com/training/package-visibility and
         https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.

         In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
    <queries>
        <intent>
            <action android:name="android.intent.action.PROCESS_TEXT"/>
            <data android:mimeType="text/plain"/>
        </intent>
    </queries>
</manifest>

코드 기재했습니다 :slight_smile:

안녕하세요.

flutter 로 웹 서비스(브라우저로 URL 접속하여)와, 앱 서비스(마켓에서 다운받아 사용하는) 둘다 제공하시는 건가요?

앗 아니요 ! 안드로이드 , ios 만 출시예정인데 카카오톡이 깔려있지 않은 경우를 대비해서 웹서비스도 구현해야한다고 알고있었어요 , !!

안녕하세요.

카카오톡이 설치되어 있지 않은 경우
SDK가 웹뷰를 통해 카카오 계정 로그인 페이지를 제공합니다.
로그인 이후 웹이 아닌 앱으로 돌아옵니다.

따라서, 달리 웹서비스를 구현하실 필요 없이 가드와 동일하게 구현하시면 됩니다.

백 서버는 스프링을 사용하고 있는데 restAPI식으로 구현했다고 하네요 ! 그래서 저희도 플러터 ( 앱 서비스) 이용해서 rest API의 redirect url로 구현해서 code를 통해 인가코드를 보내고 id 토큰을 발급받는 형식을하려고 하는데 그러면 코드를 어떻게 짜야하는지 방향을 잘 모르겠어서 문의 드립니다,.

댓글 보고 카카오톡 sdk와 restURI를 통해 받는 두 가지의 방법으로 구현했는데 KE006 ( redirect uri를 찾을 수 없음 ) 이라는 오류가 뜹니다… 도메인 구매하여 디벨로퍼에 등록해놨지만, 연결을 어디에 해야하는지 의문입니다. 백 서버는 스프링 사용중이고 아직 배포 전인 상태입니다.

안녕하세요.

회원님 서비스 백엔드에서 일반적인 redirect_uri 방식의 인증 흐름을 구현 하였지만
(redirect_uri 로 인가코드가 전달되고 접근토큰 발급 및 서비스측 후속 처리)

네이티브 앱에서 카카오가 제공하는 flutter SDK를 사용하는 경우 앱에서 이 과정이 앱에서 이루어 집니다.
(기본적으로 카카오 인증 후, 인가코드를 갖고 앱으로 돌아가게 되고 SDK가 접근토큰 발급 합니다.)

따라서, 회원님 서비스에서 백엔드측 인증 흐름을 이용할 할 필요 없이 이미 카카오 로그인 인증 된 상태에서 후속 처리를 진행하시면 될것으로 보입니다.

그러면 후속 조치 필요없이 idtoken을 발급받아도 서버측에서 idToken을 발급받는것이 아닌데도 서버(스프링) 쪽에서도 idToken값과 accessToken값이 유효한가요 ? 답장 기다리겠습니다…

백엔드에 idToken과 accessToken을 전달하시나요?

RestAPI 형식으로 구현하여, 코드를 백에게 넘겨주면 서버에서 idToken을 발급해줍니다. 그리고 idToken을 서버에게 보내면 AccessToken과 RefreshToken을 발급해줍니다. 그리고 ResfreshToken을 통해 AccessToken을 재발급받는 형식입니다.

제공되는 flutter SDK로 카카오 로그인 시 인가코드 및 접근토큰 발급까지 앱(프론트)에서 처리됩니다.
따라서 앱에서 백엔드로 전달할 인가코드는 없습니다.

refresh token, access token, id token이 백엔드에서 필요한 이유가 없을것 같은데요
이들 정보가 백엔드에 필요한지 협의 되셨을까요?

일단 스웨거에 로그인 로직에 대해 설명해 둔 부분이 플로우가 위에 말씀드린것처럼 구성되어있었습니다. 그래서 했는데 다시 얘기해보고 말씀드리겠습니다 감사합니다 !

아니면 혹시 플러터 환경에서 위에 말씀드린 방법으로 하는 방법이 또 있을까요 ?

우선, 프론트와 백엔드간 소셜로그인 인가처리(카카오 인가가 아닌 서비스측 백엔드 세션)를 어떻게 할 것인지 협의하신 뒤 구현 방식을 결정하시는 것이 좋을것 같습니다.

프론트에서 접근 토큰 발급까지 이루어지는 SDK를 사용하는 소셜 로그인의 경우 일반적으로 id_token 백엔드로 전달하여 토큰 유효성 검사 후, 회원가입 처리 및 백엔드 인가처리를 마무리 하고, 백엔드에서 redirect_uri 방식으로 처리하는 서비스의 경우는 프론트에서 자체적으로 웹뷰를 띄워 처리 합니다.

협의를 했던 것이 백엔드에서 redirect_uri 방식으로 처리하는 서비스였습니다. 그러면 프론트에서 웹뷰 형식을 ㄷ어떻게 띄워 줘야하나요 ?

서비스는 네이티브로 구성하지만, 로그인만 redirect_uri 사용하고자 하시는게 맞으실까요?
카카오 로그인과 무관하게 서비스 앱에서 웹뷰를 사용하지 않는게 맞으실까요?