본문 바로가기

Facebook Login 설정

GitHub

소개

소개

Capgo Social Login을 사용하여 Facebook Login을 설정하는 방법을 알아봅니다. 다음 사항이 필요합니다:

  • Facebook Developer 계정
  • 앱의 package name 또는 bundle ID
  • 키 해시 생성을 위한 터미널 권한 (Android)

아직 Facebook 앱을 생성하지 않았다면, 다음 단계를 따르세요:

  1. Facebook App 생성하기

    다음 튜토리얼을 따라 진행하세요: App 생성

  2. 앱에 Facebook Login 추가

    Facebook Developer Dashboard에서 앱에 Facebook Login 제품을 추가합니다.

  3. 앱을 정식 출시하기 전에, 다음 튜토리얼을 참고하여 앱을 게시하세요.

연동에 필요한 주요 정보는 여기서 확인할 수 있습니다:

  1. CLIENT_TOKEN:

    클라이언트 토큰 위치를 보여주는 Facebook developer dashboard
  2. APP_ID:

    App ID 위치를 보여주는 Facebook developer dashboard
  3. APP_NAME:

    App name 위치를 보여주는 Facebook developer dashboard

Facebook 비즈니스 로그인

“Facebook Business Login” 섹션

이 플러그인은 비즈니스 관련 기능 및 권한을 위한 Facebook Business Login을 지원합니다. 비즈니스 계정은 일반 소비자 로그인 외에도 Instagram 및 Pages 관리 등 추가 권한을 요청할 수 있습니다.

지원되는 비즈니스 권한은 다음과 같습니다:

  • instagram_basic - Instagram Basic Display API 액세스 권한
  • instagram_manage_insights - Instagram Insights 액세스 권한
  • pages_show_list - 사용자가 관리하는 Pages 목록
  • pages_read_engagement - Pages의 참여 데이터 읽기 권한
  • pages_manage_posts - Pages의 게시물 관리 권한
  • business_management - 비즈니스 자산 관리

참고하세요: 예제 앱의 Facebook 권한 참조 전체 권한 목록을 확인하려면.

설정 요구 사항:

  1. Facebook Developer Console에서 Facebook 앱을 Business 앱으로 설정해야 합니다.
  2. 비즈니스 권한은 프로덕션 사용 전 Facebook App Review가 필요할 수 있습니다.
  3. 앱은 Facebook의 Business Use Case 정책을 준수해야 합니다.
await SocialLogin.initialize({
facebook: {
appId: 'your-business-app-id',
clientToken: 'your-client-token',
},
});
const res = await SocialLogin.login({
provider: 'facebook',
options: {
permissions: [
'email',
'public_profile',
'instagram_basic',
'pages_show_list',
'pages_read_engagement',
],
},
});
const profile = await SocialLogin.providerSpecificCall({
call: 'facebook#getProfile',
options: {
fields: ['id', 'name', 'email', 'instagram_business_account'],
},
});
const res = await SocialLogin.login({
provider: 'facebook',
options: {
permissions: [
'email',
'pages_show_list',
'pages_manage_posts',
'pages_read_engagement',
],
},
});
const profile = await SocialLogin.providerSpecificCall({
call: 'facebook#getProfile',
options: {
fields: ['id', 'name', 'accounts{id,name,instagram_business_account}'],
},
});

주의 사항:

  • App Review를 받기 전, 테스트 사용자 및 개발용 앱을 통해 비즈니스 권한을 테스트할 수 있습니다.
  • 대부분의 비즈니스 권한은 프로덕션 환경에서 사용하기 전 Facebook App Review가 필요합니다.
  • Business API마다 Rate Limit이 다릅니다. 출시 전 Facebook의 최신 플랫폼 문서를 확인하세요.
  • 앱을 설정할 때는 Facebook Business Integration Guide를 참조하세요.
  1. 다음 항목에 internet permission을 추가하세요: AndroidManifest.xml

    다음 줄이 포함되어 있는지 확인하세요:

    <uses-permission android:name="android.permission.INTERNET"/>
  2. Android 키 해시 생성하기

    Facebook에서 요구하는 필수 보안 단계입니다. 터미널을 열고 다음 명령어를 실행하세요:

    터미널 창
    keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64 -A

    비밀번호 입력 프롬프트가 나오면 다음을 사용하세요: android

  3. Facebook 앱에 키 해시 추가하기

    1. Facebook Developers의 앱 대시보드로 이동하세요
    2. Settings > Basic 메뉴로 이동합니다
    3. “Android” 섹션까지 스크롤하세요
    4. Android가 없다면 “Add Platform”을 클릭해 정보를 입력하세요
    5. 생성한 키 해시를 추가합니다
    6. 프로덕션 환경을 위해 debug와 release 키 해시를 모두 추가하세요
  4. 다음 내용을 포함하도록 AndroidManifest.xml 를 업데이트하세요:

    <application>
    ...
    <activity android:name="com.facebook.FacebookActivity"
    android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
    android:label="@string/app_name" />
    <activity
    android:name="com.facebook.CustomTabActivity"
    android:exported="true">
    <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="FB[APP_ID]" />
    </intent-filter>
    </activity>
    </application>
  1. Facebook Developer Console에서 iOS 플랫폼 추가하기

    1. Facebook Developers에서 앱 대시보드로 이동하세요.
    2. Settings > Basic으로 이동하세요.
    3. 페이지 맨 아래까지 스크롤한 다음 “Add Platform”을 클릭하세요.
    4. iOS를 선택하고 필요한 정보를 입력하세요.
  2. Xcode 프로젝트를 열고 Info.plist로 이동하세요.

  3. Info.plist에 다음 항목을 추가하세요:

    <key>FacebookAppID</key>
    <string>[APP-ID]</string>
    <key>FacebookClientToken</key>
    <string>[CLIENT-TOKEN]</string>
    <key>FacebookDisplayName</key>
    <string>[APP-NAME]</string>
    <key>LSApplicationQueriesSchemes</key>
    <array>
    <string>fbapi</string>
    <string>fb-messenger-share-api</string>
    </array>
    <key>CFBundleURLTypes</key>
    <array>
    <dict>
    <key>CFBundleURLSchemes</key>
    <array>
    <string>fb[APP-ID]</string>
    </array>
    </dict>
    </array>
  4. 다음 항목을 수정하세요: AppDelegate.swift

    import FBSDKCoreKit
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    // Initialize Facebook SDK
    FBSDKCoreKit.ApplicationDelegate.shared.application(
    application,
    didFinishLaunchingWithOptions: launchOptions
    )
    return true
    }
    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
    // Called when the app was launched with a url. Feel free to add additional processing here,
    // but if you want the App API to support tracking app url opens, make sure to keep this call
    if (FBSDKCoreKit.ApplicationDelegate.shared.application(
    app,
    open: url,
    sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String,
    annotation: options[UIApplication.OpenURLOptionsKey.annotation]
    )) {
    return true;
    } else {
    return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
    }
    }
    }

앱에서 Facebook Login 사용하기

“Using Facebook Login in Your App” 섹션
  1. 앱에 Facebook 로그인 초기화하기

    import { SocialLogin } from '@capgo/capacitor-social-login';
    // Initialize during app startup
    await SocialLogin.initialize({
    facebook: {
    appId: 'APP_ID',
    clientToken: 'CLIENT_TOKEN',
    }
    })
  2. 로그인 함수 구현하기

    async function loginWithFacebook() {
    try {
    const result = await SocialLogin.login({
    provider: 'facebook',
    options: {
    permissions: ['email', 'public_profile'],
    limitedLogin: false // See Limited Login section below for important details
    }
    });
    console.log('Facebook login result:', result);
    // Handle successful login
    } catch (error) {
    console.error('Facebook login error:', error);
    // Handle error
    }
    }
  3. 사용자 프로필 데이터 가져오기

    로그인에 성공하면 다음과 같이 추가 프로필 정보를 가져올 수 있습니다:

    async function getFacebookProfile() {
    try {
    const profileResponse = await SocialLogin.providerSpecificCall({
    call: 'facebook#getProfile',
    options: {
    fields: ['id', 'name', 'email', 'first_name', 'last_name', 'picture']
    }
    });
    console.log('Facebook profile:', profileResponse.profile);
    return profileResponse.profile;
    } catch (error) {
    console.error('Failed to get Facebook profile:', error);
    return null;
    }
    }
    // Example usage after login
    async function loginAndGetProfile() {
    const loginResult = await loginWithFacebook();
    if (loginResult) {
    const profile = await getFacebookProfile();
    if (profile) {
    console.log('User ID:', profile.id);
    console.log('Name:', profile.name);
    console.log('Email:', profile.email);
    console.log('Profile Picture:', profile.picture?.data?.url);
    }
    }
    }

    토큰 유형 제한: getProfile 호출은 액세스 토큰 (트래킹이 허용된 표준 로그인)이 있는 경우에만 작동합니다. 사용자가 트래킹을 거부했거나 제한된 로그인(JWT 토큰만 사용)을 사용하는 경우 이 호출은 실패합니다. 이 경우 초기 로그인 응답에서 제공된 프로필 데이터를 사용하세요.

⚠️ 중요: 백엔드 토큰 처리

“⚠️ 주의: 백엔드 토큰 처리” 섹션

백엔드에서 다음 두 가지 서로 다른 토큰 타입을 처리해야 합니다. iOS 사용자는 App Tracking Transparency 선택에 따라 access token 또는 JWT token을 받을 수 있는 반면, Android 사용자는 항상 access token을 받기 때문입니다.

플랫폼별 토큰 유형

“플랫폼별 토큰 타입” 섹션
플랫폼limitedLogin 설정사용자 ATT 선택 사항결과 토큰 유형
iOStrue모든 플랫폼JWT 토큰
iOSfalse트래킹 허용액세스 토큰
iOSfalse트래킹 거부JWT 토큰 (자동 재정의)
Android모든 플랫폼해당 없음Access Token (항상)
  1. 토큰 유형 감지 및 처리

    async function loginWithFacebook() {
    try {
    const loginResult = await SocialLogin.login({
    provider: 'facebook',
    options: {
    permissions: ['email', 'public_profile'],
    limitedLogin: false // iOS: depends on ATT, Android: ignored
    }
    });
    if (loginResult.accessToken) {
    // Access token (Android always, iOS when tracking allowed)
    return handleAccessToken(loginResult.accessToken.token);
    } else if (loginResult.idToken) {
    // JWT token (iOS only when tracking denied or limitedLogin: true)
    return handleJWTToken(loginResult.idToken);
    }
    } catch (error) {
    console.error('Facebook login error:', error);
    }
    }
  2. Firebase 연동 예시

    import { OAuthProvider, FacebookAuthProvider, signInWithCredential } from 'firebase/auth';
    async function handleAccessToken(accessToken: string, nonce: string) {
    // For access tokens, use OAuthProvider (new method)
    const fbOAuth = new OAuthProvider("facebook.com");
    const credential = fbOAuth.credential({
    idToken: accessToken,
    rawNonce: nonce
    });
    try {
    const userResponse = await signInWithCredential(auth, credential);
    return userResponse;
    } catch (error) {
    console.error('Firebase OAuth error:', error);
    return false;
    }
    }
    async function handleJWTToken(jwtToken: string) {
    // For JWT tokens, send to your backend for validation
    try {
    const response = await fetch('/api/auth/facebook-jwt', {
    method: 'POST',
    headers: {
    'Content-Type': 'application/json',
    },
    body: JSON.stringify({ jwtToken })
    });
    const result = await response.json();
    return result;
    } catch (error) {
    console.error('JWT validation error:', error);
    return false;
    }
    }
  3. Backend JWT 검증

    // Backend: Validate JWT token from Facebook
    import jwt from 'jsonwebtoken';
    import { Request, Response } from 'express';
    app.post('/api/auth/facebook-jwt', async (req: Request, res: Response) => {
    const { jwtToken } = req.body;
    try {
    // Verify JWT token with Facebook's public key
    // See: https://developers.facebook.com/docs/facebook-login/limited-login/token/validating/#standard-claims
    const decoded = jwt.verify(jwtToken, getFacebookPublicKey(), {
    algorithms: ['RS256'],
    audience: process.env.FACEBOOK_APP_ID,
    issuer: 'https://www.facebook.com' // From: https://www.facebook.com/.well-known/openid-configuration/?_rdr
    });
    // Extract user info from JWT
    const userInfo = {
    id: decoded.sub,
    email: decoded.email,
    name: decoded.name,
    isJWTAuth: true
    };
    // Create your app's session/token
    const sessionToken = createUserSession(userInfo);
    res.json({
    success: true,
    token: sessionToken,
    user: userInfo
    });
    } catch (error) {
    console.error('JWT validation failed:', error);
    res.status(401).json({ success: false, error: 'Invalid token' });
    }
    });
  4. 범용 백엔드 토큰 핸들러

    // Handle both token types in your backend
    async function authenticateFacebookUser(tokenData: any) {
    if (tokenData.accessToken) {
    // Handle access token - validate with Facebook Graph API
    const response = await fetch(`https://graph.facebook.com/me?access_token=${tokenData.accessToken}&fields=id,name,email`);
    const userInfo = await response.json();
    return {
    user: userInfo,
    tokenType: 'access_token',
    expiresIn: tokenData.expiresIn || 3600
    };
    } else if (tokenData.jwtToken) {
    // Handle JWT token - decode and validate
    // See: https://developers.facebook.com/docs/facebook-login/limited-login/token/validating/#standard-claims
    const decoded = jwt.verify(tokenData.jwtToken, getFacebookPublicKey());
    return {
    user: {
    id: decoded.sub,
    name: decoded.name,
    email: decoded.email
    },
    tokenType: 'jwt',
    expiresIn: decoded.exp - Math.floor(Date.now() / 1000)
    };
    } else {
    throw new Error('No valid token provided');
    }
    }

액세스 토큰 (Standard Login):

  • ✅ Android: 항상 사용 가능 (iOS 전용 제한 미적용)
  • ✅ iOS: 사용자가 앱 추적을 명시적으로 허용할 때만 가능
  • ✅ Facebook Graph API 접근 가능
  • ✅ 더 긴 만료 시간
  • ✅ 더 많은 사용자 데이터 활용 가능
  • ❌ iOS에서 사용 빈도가 낮아지는 추세 사용자들이 추적 허용을 거부하는 사례가 늘고 있기 때문

JWT Token (iOS 전용 개인정보 보호 모드):

  • ❌ Android: 발생하지 않음 (미지원)
  • ✅ iOS: 추적이 거부된 경우 또는 limitedLogin: true
  • ✅ iOS 사용자 개인정보 보호 설정 준수
  • ❌ 기본 사용자 정보만 포함
  • ❌ 짧은 만료 시간
  • ❌ Facebook Graph API 접근 불가
  • ⚠️ 이제 iOS 사용자에게 가장 흔한 시나리오

플랫폼별 동작 방식:

  • iOS 앱: access token과 JWT token을 모두 처리해야 합니다
  • Android 앱: access token만 처리하면 됩니다
  • 크로스 플랫폼 앱: 두 가지 token 처리 방식을 모두 구현해야 합니다

보안 컨텍스트 요구 사항 (Web/Capacitor)

“보안 컨텍스트 요구 사항 (Web/Capacitor)” 섹션

업데이트된 Facebook 로그인 흐름에는 Web Crypto API nonce 생성을 위한 기능이 필요하며, 이는 오직 secure contexts에서만 사용 가능합니다:

// This requires secure context (HTTPS or localhost)
async function sha256(message: string) {
const msgBuffer = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer); // ❌ Fails in insecure context
// ...
}

일반적인 문제: ionic serve HTTP URL 사용 시 Facebook 인증 오류 발생

환경Crypto API 사용 가능 여부Facebook Login 작동 여부
http://localhost:3000✅ 작동함✅ 작동함
http://127.0.0.1:3000✅ 작동함✅ 작동함
http://192.168.1.100:3000❌ 작동 안 함❌ 작동 안 함
https://any-domain.com✅ 작동함✅ 작동함

Capacitor 개발을 위한 솔루션

“Capacitor 개발을 위한 솔루션” 섹션
  1. 웹 테스트 시 localhost 사용

    터미널 창
    # Instead of ionic serve --host=0.0.0.0
    ionic serve --host=localhost
  2. Ionic에서 HTTPS 활성화하기

    터미널 창
    ionic serve --ssl
  3. 실제 기기에서 테스트하기

    터미널 창
    # Capacitor apps run in secure context on devices
    ionic cap run ios
    ionic cap run android
  4. 개발용 대체 nonce 생성 방식

    async function generateNonce() {
    if (typeof crypto !== 'undefined' && crypto.subtle) {
    // Secure context - use crypto.subtle
    return await sha256(Math.random().toString(36).substring(2, 10));
    } else {
    // Fallback for development (not secure for production)
    console.warn('Using fallback nonce - not secure for production');
    return btoa(Math.random().toString(36).substring(2, 10));
    }
    }

Firebase 연동 참고 사항

“Firebase 연동 참고 사항” 섹션

최근 Firebase 문서를 보면 로그인 설정과 관계없이 Facebook 인증 시 nonce가 포함된 JWT 토큰을 요구합니다. 이 방식은 다음 두 경우 모두에 적용됩니다. limitedLogin: true 및 limitedLogin: false:

// Both modes can return JWT tokens depending on user choice
const loginResult = await SocialLogin.login({
provider: 'facebook',
options: {
permissions: ['email', 'public_profile'],
limitedLogin: false, // true = always JWT, false = depends on user tracking choice
nonce: nonce
}
});

개발 시 제한 사항: 만약 ionic serve 를 네트워크 IP(localhost 제외)에서 사용 중이라면, crypto API 제한으로 인해 Facebook 로그인이 실패합니다. 웹 테스트 시에는 localhost 또는 HTTPS를 사용하세요.

자주 발생하는 문제 및 해결 방법

“자주 발생하는 문제 및 해결 방법” 섹션
  1. Android 키 해시(Key hash) 오류

    • Facebook 대시보드에 올바른 키 해시를 추가했는지 다시 확인하세요
    • Release 빌드의 경우, debug와 release 키 해시를 모두 추가했는지 확인하세요
    • 해시 생성 시 올바른 keystore를 사용 중인지 확인하세요
  2. Facebook 로그인 버튼이 나타나지 않음

    • 모든 manifest 항목이 올바른지 확인하세요
    • Facebook App ID와 Client Token이 정확한지 확인하세요
    • SDK가 제대로 초기화되었는지 확인하세요
  3. 자주 발생하는 iOS 문제

    • 모든 Info.plist 항목이 올바른지 확인하세요
    • URL scheme 설정 확인
    • Bundle ID가 Facebook dashboard에 등록된 값과 일치하는지 확인하세요
  1. 테스트 전, Facebook Developer Console에서 테스트 사용자를 추가하세요

    • Roles > Test Users로 이동
    • 테스트 사용자 생성
    • 테스트 시 다음 계정 정보를 사용하세요
  2. Debug 및 Release build 모두 테스트하세요

    • Debug key hash를 사용한 Debug build
    • Release key hash를 사용한 Release build
    • 에뮬레이터와 실기기 모두에서 테스트하세요

다음 항목을 포함하여 전체 로그인 흐름을 반드시 테스트하세요:

  • 로그인 성공
  • 로그인 취소
  • 에러 처리
  • 로그아웃 기능

Facebook Login Setup 단계 계속하기

“Facebook Login Setup 단계 계속하기” 섹션

사용 중이라면 Facebook Login 설정 인증 및 계정 흐름을 설계할 때, 이를 다음 항목과 연결하세요: @capgo/capacitor-social-login 사용하기 @capgo/capacitor-social-login를 사용하여 네이티브 기능을 구현하는 경우, @capgo/capacitor-social-login @capgo/capacitor-social-login의 구현 세부 사항은, @capgo/capacitor-passkey @capgo/capacitor-passkey의 구현 세부 사항은 다음을 참고하세요, @capgo/capacitor-native-biometric @capgo/capacitor-native-biometric의 구현 세부 사항은, 2단계 인증 2단계 인증의 구현 세부 사항에 대해서는