Saltar al contenido

Facebook Login Setup

Este contenido aún no está disponible en tu idioma.

Introduction

In this guide, you will learn how to setup Facebook Login with Capgo Social Login. You will need the following:

  • A Facebook Developer Account
  • Your app’s package name/bundle ID
  • Access to a terminal for generating key hashes (Android)

General Setup

If you don’t already have a Facebook app created, follow these steps:

  1. Create a Facebook App

    Follow the tutorial to Create an App

  2. Add Facebook Login to your app

    In your Facebook Developer Dashboard, add the Facebook Login product to your app

  3. Before you can release your app to the public, follow this tutorial to publish it

Important Information

Here’s where to find the key information you’ll need for integration:

  1. CLIENT_TOKEN:

    Facebook developer dashboard showing where to find the client token
  2. APP_ID:

    Facebook developer dashboard showing where to find the app ID
  3. APP_NAME:

    Facebook developer dashboard showing where to find the app name

Android Setup

  1. Add internet permission to your AndroidManifest.xml

    Ensure this line is present:

    <uses-permission android:name="android.permission.INTERNET"/>
  2. Generate your Android key hash

    This is a crucial security step required by Facebook. Open your terminal and run:

    Terminal window
    keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl base64 -A

    When prompted for a password, use: android

  3. Add the key hash to your Facebook app

    1. Go to your app’s dashboard on Facebook Developers
    2. Navigate to Settings > Basic
    3. Scroll down to “Android” section
    4. Click “Add Platform” if Android isn’t added yet and fill in the details
    5. Add the key hash you generated
    6. For production, add both debug and release key hashes
  4. Update your AndroidManifest.xml to include:

    <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>

iOS Setup

  1. Add the iOS platform in Facebook Developer Console

    1. Go to your app’s dashboard on Facebook Developers
    2. Navigate to Settings > Basic
    3. Scroll down to very bottom of the page and click “Add Platform”
    4. Select iOS and fill in the required details
  2. Open your Xcode project and navigate to Info.plist

  3. Add the following entries to your 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. Modify the 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)
    }
    }
    }

Using Facebook Login in Your App

  1. Initialize the Facebook login in your app

    import { SocialLogin } from '@capgo/capacitor-social-login';
    // Initialize during app startup
    await SocialLogin.initialize({
    facebook: {
    appId: 'APP_ID',
    clientToken: 'CLIENT_TOKEN',
    }
    })
  2. Implement the login function

    async function loginWithFacebook() {
    try {
    const result = await SocialLogin.login({
    provider: 'facebook',
    options: {
    permissions: ['email', 'public_profile'],
    limitedLogin: false // this will depend if you want to use the limited login or not
    }
    });
    console.log('Facebook login result:', result);
    // Handle successful login
    } catch (error) {
    console.error('Facebook login error:', error);
    // Handle error
    }
    }
  3. Get User Profile Data

    After successful login, you can retrieve additional profile information:

    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);
    }
    }
    }

    Token Type Limitation: The getProfile call only works when you have an access token (standard login with tracking allowed). If the user denied tracking or you’re using limited login (JWT token only), this call will fail. In that case, use the profile data provided in the initial login response.

⚠️ Critical: Understanding Facebook Login Token Types

Facebook login behavior has changed significantly, especially on iOS with App Tracking Transparency (ATT). Your backend must handle two different token types depending on user permissions and tracking settings.

Token Types Explained

Facebook now returns different token types based on what you request and user consent:

Your Request + User ChoiceWhat You GetBackend Handling
limitedLogin: trueJWT TokenValidate as ID token
limitedLogin: false + User ALLOWS trackingAccess TokenUse as OAuth credential
limitedLogin: false + User DENIES trackingJWT TokenValidate as ID token

Key Points:

  • If you set limitedLogin: trueAlways JWT token (guaranteed)
  • If you set limitedLogin: falseUser choice determines the result (access token OR JWT token)
  • You cannot force an access token if tracking is denied

iOS App Tracking Transparency Impact

On iOS, the user’s App Tracking Transparency choice directly affects the token type you receive:

  • Tracking Allowed: You get an access token (if not using limited login)
  • Tracking Denied: Automatically falls back to JWT token, regardless of your settings

ATT Status Values

When requesting tracking permission, you’ll receive one of these status values:

StatusDescriptionLogin Behavior
authorizedUser granted tracking permissionWill receive access token
deniedUser explicitly denied trackingFalls back to JWT token
notDeterminedUser hasn’t been asked yetShows ATT prompt, then returns updated status
restrictedTracking restricted by device settingsFalls back to JWT token

Implementation with Token Detection

  1. Frontend Implementation with ATT Check

    import { SocialLogin } from '@capgo/capacitor-social-login';
    import { Capacitor } from '@capacitor/core';
    async function loginWithFacebook() {
    // Check ATT permission on iOS
    let proceed = true;
    if (Capacitor.getPlatform() === 'ios') {
    const trackingResponse = await SocialLogin.providerSpecificCall({
    call: 'facebook#requestTracking',
    options: {}
    });
    // Only proceed if user authorized tracking
    proceed = trackingResponse.status === 'authorized';
    if (!proceed) {
    console.log(`App tracking status: ${trackingResponse.status}`);
    // You can still attempt login - it will fall back to JWT
    }
    }
    // Generate nonce for security
    const nonce = Math.random().toString(36).substring(2, 10);
    const hashedNonceHex = await sha256(nonce);
    try {
    const loginResult = await SocialLogin.login({
    provider: 'facebook',
    options: {
    permissions: ['email', 'public_profile'],
    limitedLogin: false, // true for limited login, false for standard tracking
    nonce: nonce
    }
    });
    // ⚠️ IMPORTANT: Token type behavior:
    // - limitedLogin: true → ALWAYS JWT token (guaranteed)
    // - limitedLogin: false → depends on user's App Tracking choice
    if (loginResult.accessToken) {
    // Access token - user allowed tracking
    console.log('Received access token:', loginResult.accessToken.token);
    return handleAccessToken(loginResult.accessToken.token, nonce);
    } else if (loginResult.idToken) {
    // JWT token - user denied tracking, opted out at OS level, or limited login
    console.log('Received JWT token:', loginResult.idToken);
    return handleJWTToken(loginResult.idToken);
    } else {
    console.log('Facebook login failed - no token received');
    return false;
    }
    } catch (error) {
    console.error('Facebook login error:', error);
    return false;
    }
    }
    async function sha256(message: string) {
    const msgBuffer = new TextEncoder().encode(message);
    const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    const hashHex = hashArray
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
    return hashHex;
    }
  2. Firebase Integration Example

    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 Validation

    // 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
    const decoded = jwt.verify(jwtToken, getApplePublicKey(), {
    algorithms: ['RS256'],
    audience: process.env.FACEBOOK_APP_ID,
    issuer: 'https://appleid.apple.com'
    });
    // 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. Generic Backend Token Handler

    // 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
    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');
    }
    }

Key Considerations

Access Token (Standard Login):

  • ✅ Only available when user explicitly allows app tracking on iOS
  • ✅ Can be used to access Facebook Graph API
  • ✅ Longer expiration times
  • ✅ More user data available
  • Becoming less common as users increasingly deny tracking

JWT Token (New Default):

  • ✅ Always available regardless of tracking permissions
  • ✅ Respects user privacy preferences
  • ❌ Contains basic user info only
  • ❌ Shorter expiration times
  • ❌ No access to Facebook Graph API or additional profile data
  • ⚠️ Now the most common scenario with iOS users

Important: The old method using FacebookAuthProvider.credential(accessToken) no longer works because Facebook now returns JWT tokens in the majority of cases. Always check the token type first.

Secure Context Requirements (Web/Capacitor)

Crypto API Limitations

The updated Facebook login flow requires the Web Crypto API for nonce generation, which is only available in 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
// ...
}

Development Environment Issues

Common Problem: ionic serve with HTTP URLs breaks Facebook authentication

EnvironmentCrypto API AvailableFacebook Login Works
http://localhost:3000✅ Yes✅ Yes
http://127.0.0.1:3000✅ Yes✅ Yes
http://192.168.1.100:3000❌ No❌ No
https://any-domain.com✅ Yes✅ Yes

Solutions for Capacitor Development

  1. Use localhost for web testing

    Terminal window
    # Instead of ionic serve --host=0.0.0.0
    ionic serve --host=localhost
  2. Enable HTTPS in Ionic

    Terminal window
    ionic serve --ssl
  3. Test on actual devices

    Terminal window
    # Capacitor apps run in secure context on devices
    ionic cap run ios
    ionic cap run android
  4. Alternative nonce generation for development

    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 Integration Note

Recent Firebase documentation requires JWT tokens with nonces for Facebook authentication, regardless of login settings. This approach works with both limitedLogin: true and 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
}
});

Development Limitation: If you’re using ionic serve on a network IP (not localhost), Facebook login will fail due to crypto API restrictions. Use localhost or HTTPS for web testing.

Troubleshooting

Common Issues and Solutions

  1. Key hash errors on Android

    • Double check that you’ve added the correct key hash to the Facebook dashboard
    • For release builds, make sure you’ve added both debug and release key hashes
    • Verify you’re using the correct keystore when generating the hash
  2. Facebook login button doesn’t appear

    • Verify all manifest entries are correct
    • Check that your Facebook App ID and Client Token are correct
    • Ensure you’ve properly initialized the SDK
  3. Common iOS issues

    • Make sure all Info.plist entries are correct
    • Verify URL schemes are properly configured
    • Check that your bundle ID matches what’s registered in the Facebook dashboard

Testing

  1. Before testing, add test users in the Facebook Developer Console

    • Go to Roles > Test Users
    • Create a test user
    • Use these credentials for testing
  2. Test both debug and release builds

    • Debug build with debug key hash
    • Release build with release key hash
    • Test on both emulator and physical devices

Remember to test the full login flow, including:

  • Successful login
  • Login cancellation
  • Error handling
  • Logout functionality