flutter와 크리덴셜 방식을 이용한 카카오 OAuth 실습

박선규's avatar
Jun 05, 2024
flutter와 크리덴셜 방식을 이용한 카카오 OAuth 실습
 

셋팅

1.설치

해당 최신버전에 맞는 sdk 넣어주기
해당 최신버전에 맞는 sdk 넣어주기
 

2.초기화

notion image
네이티브 앱 서비스는 리다이렉트를 사용할 수 없다는 경고가 뜨는데
라이브러리를 쓰지 않으면 가능

2.1 내 프로젝트 생성

notion image
notion image
패키지 명은
패키지 명은
 
실행 패키지
실행 패키지
notion image
 
패키지 명은 내가 원하는 대로 설정하면 안 된다. build.gradle 파일에서 패키지명을 확인하고 등록해야 한다.
notion image
 
 
 

2.2 앱 키 발급 및 적용

notion image
notion image
 

3. 키 해시 (앱 전자 서명)

notion image
앱에 서명이 필요하다. (서명이 된 앱만 플레이스토어에 등록된 후 카카오 로그인을 할 수 있다.)
개념
앱스토어에 안드로이드 앱이 하나 올라 갈텐데
여기는 그림 코드와 통신 코드가 올라간다.
notion image
등록을 하면 사람들이 다운 받을 텐 데.
앱을 다운 = 그림 전체를 다운
이렇다면 이동 할 때 페이지에 필요한 데이터만 서버에서 요청해서 받으면 된다
이때 A휴대폰과 B휴대폰에서 다운 받으면
notion image
앱 스토어에 등록하지 않으면 도촬 가능 하다.(보안 검사 1도 안됨)
앱 스토어에서 자체적으로 등록 시에 검사하여 잘못 된 게 있으면 막아 주기 때문이다.
 
홍길동이 툴을 개발 했다
notion image
카카오 로그인을 하고 싶으면 카카오에 이 툴을 검증을 받아야 한다.
웹을 나중에 등록을 하면 DNS주소가 필요한데 (이유는 도메인 주소로 검사를 하기때문이다.
 
장보고도 자기 툴을 동일하게 카카오 로그인 적용해서 개발하고 인증 안된 상태에 등록하지 않은 채 직접 구워 배포를 하면 불법인데
notion image
카카오 로그인이 되면 안된다.
 
내 프로젝트에 있는 모든 코드를 굽는데
코드를 해시로 굽는다…이게 키 해쉬다.
notion image
 
notion image
이렇게 해시를 등록 하면 장보고는 해시가 없기때문에 휴대폰에서 툴을 다운 받아 카카오 로그인을 할 때 불법인지 카카오에서 쉽게 거를 수 있다.
notion image
 
이제부터 실습을오 해볼껀데
먼저 앱을 굽는다
notion image
카카오에서 주는 디버그 해시를 등록해야 한다.
→ 배포한다면 릴리즈 해시를 달아서 배포해야 한다.
keytool -exportcert -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore -storepass android -keypass android | openssl sha1 -binary | openssl base64keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -storepass android -keypass android | openssl sha1 -binary | openssl base64
cmd - 디버그 키 해시( -는 옵션을, %%는 환경변수를 의미한다.)
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -storepass android -keypass android | openssl sha1 -binary | openssl base64
git bash - 디버그 키 해시(~/는 리눅스에서 사용자 폴더를 의미한다.)
 
 
  • 키툴 있는지 확인하기
notion image
관리자 모드로 키툴 있는지 확인 만약 없으면 구굴링해서 설치 후 환경변수 까지 잡기
관리자 모드로 키툴 있는지 확인 만약 없으면 구굴링해서 설치 후 환경변수 까지 잡기
 
  • USERPROFILE 환경변수 잡혀있는지 확인하기
notion image
 
깃 배쉬는 리눅스 기반이라 안될 수 도있다.
📌
cmd가 아닌 git bash에서 한다면?
notion image
notion image
notion image
git bash는 리눅스 기반이라서 %%\가 안 먹는다. ⇒ git bash에서 환경변수를 불러올 때는 %%가 아닌 ${}를 사용해야 한다.
D 드라이브로 이동해서 만들려고하면 openssl이 안잡혀있다고 뜨는데..,….
D 드라이브로 이동해서 만들려고하면 openssl이 안잡혀있다고 뜨는데..,….
 
  • openssl 세팅하기
지금 하는 작업: 키를 BASE 64로 인코딩 하는거다.
notion image
다운받고 환경 변수 잡아주기
notion image
notion image
notion image
이렇게 키 해시가 나온다.
이렇게 키 해시가 나온다.
notion image
 

4.URL 스킴 등록(Android)

내 애플리케이션 - 앱 설정 - 앱 키에서 네이티브 앱 키를 복사 한다.
notion image
<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" /> <!-- "kakao${YOUR_NATIVE_APP_KEY}://oauth" 형식의 앱 실행 스킴 설정 --> <!-- 카카오 로그인 Redirect URI --> <data android:scheme="kakaobb232a2ba1ad3806f117271ac8122d96" android:host="oauth"/> </intent-filter> </activity>
AndroidManifest.xml에 위 코드를 추가한다.
notion image
앞의 kakao는 그대로 두고 표시한 부분에 나의 네이티브 앱 키를 넣는다.
 

5.카카오 크리덴셜 로그인 방식 사용

5.1 버전 21이상으로 잡기

notion image
notion image
JAVA(21)버전 부터 권한에 대한 체크를 안하면 앱이 안돌아가게끔 설정이 돼있다.
외부 라이브러리 = 서드 파티 라이브러리
 
 

5.2 카카오 로그인 활성화

notion image

5.3 동의 항목 설정 및 카카오 로그인 활성화

notion image
 

5.4 코드

import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:kakao_flutter_sdk/kakao_flutter_sdk.dart'; import 'package:oauthapp/_core/move.dart'; import 'ui/login_page/login_page.dart'; GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); void main() { WidgetsFlutterBinding.ensureInitialized(); // runApp() 호출 전 Flutter SDK 초기화 KakaoSdk.init( // 웹 환경에서 카카오 로그인을 정상적으로 완료하려면 runApp() 호출 전 아래 메서드 호출 필요 nativeAppKey: '32aabd0174910c36860841fa57583ef7' ); // runApp() 호출 전 Flutter SDK 초기화 runApp(ProviderScope(child: MyApp())); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( navigatorKey: navigatorKey, debugShowCheckedModeBanner: false, home: LoginPage(), routes: getRouters(), ); } }
main.dart
import 'package:flutter/material.dart'; import 'package:kakao_flutter_sdk/kakao_flutter_sdk.dart'; class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("카카오 로그인")), body: Column( children: [ ElevatedButton( onPressed: () async { try { OAuthToken token = await UserApi.instance .loginWithKakaoAccount(); print('카카오톡으로 로그인 성공 ${token.accessToken}'); } catch (error) { print('카카오톡으로 로그인 실패 $error'); } }, child: Text("카카오 로그인"), ) ], ), ); } }
login_page.dart
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:label="oauthapp" 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" /> <!-- "kakao${YOUR_NATIVE_APP_KEY}://oauth" 형식의 앱 실행 스킴 설정 --> <!-- 카카오 로그인 Redirect URI --> <data android:scheme="kakao51954f8d3d73cbf57597140dbcbee7f6" android:host="oauth"/> </intent-filter> </activity> <activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" 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?hl=en 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>
AndroidManifest.xml
 

세팅 후

notion image
try { OAuthToken token = await UserApi.instance.loginWithKakaoAccount(); print('카카오계정으로 로그인 성공 ${token.accessToken}'); } catch (error) { print('카카오계정으로 로그인 실패 $error'); }
통신을 하기 때문에 await 걸어놨다. (물론 메서드 자체는 비동기적으로 돌아간다.)
로그인이 성공되고 accessToken을 받으면 이걸 스프링 서버에게 전달해야한다.

1. 카카오로 로그인(카카오에게 직접 토큰 받기)

import 'package:flutter/material.dart'; import 'package:kakao_flutter_sdk/kakao_flutter_sdk.dart'; class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("카카오 로그인")), body: Column( children: [ ElevatedButton( onPressed: () async { try { OAuthToken token = await UserApi.instance .loginWithKakaoAccount(); print('카카오톡으로 로그인 성공 ${token.accessToken}'); } catch (error) { print('카카오톡으로 로그인 실패 $error'); } }, child: Text("카카오 로그인"), ) ], ), ); } }
login_page.dart
notion image
 
notion image
notion image
성공하면 밑에 access token이 뜨는데 이걸 secureStorage에 저장해야 한다.
 
개념
클라이언트 프로그램에 토큰을 주는 것의 여부에 따라 코드 방식인지 아닌지 갈린다.
 
이번에 한 OAuth는 그냥 은행에 가서 중개인한테 위임하고 싶다고 하니까 은행에서 토큰을 준다. 이 토큰을 중개인한테 주면서 이 토큰을 쓰면 아마 될거야 라고 한다. 중개인은 진짜 되나? 확인하려고 은행에 토큰 주고 신뢰받으면 중개인과 통신??(은행을 검증하는 용도로 사용)
코드방식: 중개인이 주인에게 받은 코드를 은행에 가져갔을 때 토큰을 받는다.
크리데셜:클라이언트가 은행에 토큰을 직접 받고 중개인에게 토큰을 넘겨주는 시스템
 
 

2. 토큰을 스프링 서버에게 전달

import 'package:flutter/material.dart'; import 'package:kakao_flutter_sdk/kakao_flutter_sdk.dart'; class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("카카오 로그인")), body: Column( children: [ ElevatedButton( onPressed: () async { kakaoLogin(); }, child: Text("카카오 로그인"), ) ], ), ); } } void kakaoLogin() async{ try { OAuthToken token = await UserApi.instance .loginWithKakaoAccount(); print('카카오톡으로 로그인 성공 ${token.accessToken}'); } catch (error) { print('카카오톡으로 로그인 실패 $error'); } }
버튼의 기능을 밖으로 빼줘서 메서드 하나만 호출해도 기능이 잘 작동하도록 수정하였다.
void kakaoLogin() async{ try { // 1. 크리덴셜 로그인 - 토근 받기 OAuthToken token = await UserApi.instance .loginWithKakaoAccount(); // qRR-UeowO-LEp0_KXAGH41vyG64QiCkJAAAAAQopyV8AAAGP3Jb1gxamEcnPBcmr print('카카오톡으로 로그인 성공 ${token.accessToken}'); // 2. 토큰(카카오)을 스프링 서버에 전달하기 (스프링 서버에게 나 인증했어! 라고 알려주는 과졍) // 3. 토큰(스프링 서버) 응답 받기 // 4. 시큐어 스토리지에 저장 } catch (error) { print('카카오톡으로 로그인 실패 $error'); } }
이제 토큰을 스프링 서버에게 전달해보자.
 

3. 고객한테 받은 토큰을 카카오에 던져 검증하기

notion image
notion image
notion image
token.해보면 위 항목들이 있다.
notion image
이게 회원정보 요청하는 코드다.
 
notion image
notion image
stl이라서 토큰만 탈취하면 정보 얻는게 가능해진다.
 
  • 서버
package shop.mtcoding.blog.user; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import java.sql.Timestamp; public class KakaoResponse { @Data public static class KakaoUserDTO { private Long id; @JsonProperty("connected_at") private Timestamp connectedAt; private Properties properties; @Data class Properties { private String nickname; } } }
/user/KakaoResponse.java
 
  • flutter(androidstudio)
notion image
notion image
내 로컬 주소 넣기
내 로컬 주소 넣기
📌
baseUrl에 내 IP를 넣어야 한다.
 
  • 서버2
사용자 정보DTO 코드 넣기
notion image
 
 
서버에 토큰 던지기
서버에 토큰 던지기

개념 재적립

코드방식: 중개인이 주인에게 받은 코드를 은행에 가져갔을 때 토큰을 받는다.
우리가 적용한 코드에 크리데셜:클라이언트가 은행에 토큰을 직접 받고 중개인에게 토큰을 넘겨주는 시스템 중개인이 다시 대체 토큰을 발행해서 클라이언트에게 준다.(이유는 이렇게안하면 중개인이 매번 은행에가서 클라이언트에게서 받은 토큰이 은행 토큰인지 검증해야 하기 때문이다)
 
잡혀있어야 하는 개념
  1. 해시
  1. 전자서명
  1. 토큰
토큰은 서버가 상태를 안 가져도 된다.
 
해시는 임의의 16진수 난수이며 고정길이를 가진다.
해시는 불변이나 변화 감지할 때 쓰인다.
해시값을 공인된 기관에 등록해야 해시.
그래야 검사할 때 공인된 기관에서 들고와서 검증이 가능하기 때문에
 
 
전자서명: 원본(해쉬) ≠ 복제(해쉬)
 
해시는 암호화를 위해 쓰는게 아니다 .전자 서명을 위해 쓰는거다.
 
 
웹 자체는 stateless 기반이다. 그래서 중요한 정보를 요청할 때마다 로그인하고 응답받아야 한다. 응답 받고 나서 그 다음에 요청하면 기억 못해서 다른 사람이 요청한다고 생각한다.
응답 할 때 id(쿠키)를 기
이거 때문에 stateful서버가 된다.
 
 
토큰: 서버를 statelee로 바꾸는거당ㄹ
서버 앞에 LB가 있는데 로그인 요청이 오면 서버에 id 카드를 하나 만든다.
이게 한명일 때 말고 200명일 때는 서버 확장이 안된다.
 
 
앱일 경우:
서버 하나로 돌리기 위해
앱이랑 웹이랑 같이 하려면 토큰 방식으로 동일화 시킨다.
이러면 서버가 토큰(사용자 정보 및 전자서명)
 
OAuth 프로토콜:내 권한을 위임
서버가 카카오에게 접근 할 수 있는 것
 
 
 
 

쌤 이 정리하신 flutter 카카오 OAuth

Share article

p4rksk