跳过内容

Facebook 登录设置

GitHub

在本指南中,您将学习如何设置 Facebook 登录与 Capgo 社交登录。您需要以下内容:

  • Facebook 开发者账户
  • 您的应用程序的包名/捆绑 ID
  • 访问终端以生成密钥哈希(Android)

如果您尚未创建 Facebook 应用程序,请遵循以下步骤:

  1. 创建 Facebook 应用

    按照教程进行 创建应用

  2. 将 Facebook 登录添加到您的应用中

    在 Facebook 开发者控制台中,添加 Facebook 登录产品到您的应用中

  3. 在发布应用到公众之前,按照以下 教程 发布应用

以下是您需要的整合所需的关键信息的位置:

  1. CLIENT_TOKEN:

    Facebook 开发者控制台显示客户端令牌位置
  2. APP_ID:

    Facebook开发者控制台显示如何找到应用ID
  3. APP_NAME:

    Facebook开发者控制台显示如何找到应用名称

Android设置

Android设置
  1. 为您的 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开发者平台上的您的应用的控制台
    2. 导航到设置>基本
    3. 滚动到底部到“Android”部分
    4. 如果Android尚未添加,请点击“添加平台”并填写详细信息
    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开发者控制台中添加iOS平台

    1. 前往您的应用程序的Facebook开发者控制台
    2. 前往设置 > 基本
    3. 将鼠标滚轮滚动到页面底部并点击“添加平台”
    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登录

标题为“在您的应用程序中使用Facebook登录”
  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);
    }
    }
    }

    Token Type Limitation:需要 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: Backend Token Handling

⚠️ Critical: Backend Token Handling

__CAPGO_KEEP_0__ 两个不同的令牌类型 因为 iOS 用户可以根据他们的 App 跟踪透明度选择,收到访问令牌或 JWT 令牌,而 Android 用户始终收到访问令牌。

令牌类型根据平台

平台令牌类型
平台有限登录设置用户 ATT 选择结果令牌类型
iOStrue任意JWT 令牌
iOSfalse允许跟踪访问令牌
iOSfalse拒绝跟踪JWT Token (auto-override)
Android任何N/A访问令牌 (始终)

后端实现

后端实现部分
  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. 后端 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');
    }
    }

重要考虑因素

标题:重要考虑因素

标准登录令牌(Access Token):

  • Android:始终可用(iOS-only限制不适用)
  • iOS:仅当用户明确允许应用跟踪时才可用
  • ✅ 可以用来访问 Facebook Graph API
  • ✅ 更长的过期时间
  • ✅ 可用更多用户数据
  • 在 iOS 上越来越少见 因为用户越来越多地拒绝跟踪

JWT Token (iOS-Only Privacy Mode):

  • Android: 从未发生 (不受支持)
  • iOS: 当跟踪被拒绝或 limitedLogin: true
  • ✅ 遵守 iOS 用户隐私偏好
  • ❌ 只包含基本用户信息
  • ❌ 简化的过期时间
  • ❌ 无法访问 Facebook Graph API
  • ⚠️ 现在是 iOS 用户最常见的场景

平台特定行为:

  • iOS 应用:必须处理两种类型的令牌:访问令牌和 JWT 令牌
  • Android 应用:只需处理访问令牌
  • 跨平台应用:必须实现两种令牌处理方法

Web 安全上下文要求 (Web/Capacitor)

安全上下文要求 (Web/Capacitor)

加密 API 限制

加密 API 限制

更新的 Facebook 登录流程需要 Web Crypto API 用于生成随机数的 仅在安全上下文中可用:

// 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 登录正常
http://localhost:3000✅ 是✅ 是
http://127.0.0.1:3000✅ 是✅ 是
http://192.168.1.100:3000❌ 否❌ 否
https://any-domain.com✅ 是✅ 是

Capacitor 开发的解决方案

Capacitor 开发解决方案的标题
  1. 使用localhost进行web测试

    终端窗口
    # 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. 开发环境的替代非对称数生成

    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 文档要求 JWT 令牌包含非对称数以支持 Facebook 登录,哪怕是无论登录设置如何。这种方法适用于 limitedLogin: truelimitedLogin: 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 上使用(而不是 localhost),Facebook 登录将因加密 API 限制而失败。请使用 localhost 或 HTTPS 进行 web 测试。

  1. Android 上的密钥哈希错误

    • 检查一下您是否在 Facebook 控制台中添加了正确的密钥哈希
    • 对于发布版本,请确保您添加了 debug 和 release 两种密钥哈希
    • 在生成哈希时,请使用正确的 keystore
  2. Facebook 登录按钮未显示

    • 验证所有清单条目是否正确
    • 检查您的Facebook App ID和Client Token是否正确
    • 确保您已正确初始化SDK
  3. 常见iOS问题

    • 确保所有Info.plist条目是否正确
    • 验证URL方案是否正确配置
    • 检查您的bundle ID是否与Facebook控制台中注册的ID匹配

测试

测试
  1. 在测试之前,在Facebook开发者控制台中添加测试用户

    • 转到角色>测试用户
    • 创建测试用户
    • 使用这些凭据进行测试
  2. 测试调试和发布版本

    • 使用调试密钥哈希的调试版本
    • 使用发布密钥哈希的发布版本
    • 在模拟器和物理设备上都进行测试

请记住测试完整的登录流程,包括:

  • 登录成功
  • 登录取消
  • 错误处理
  • 注销功能

从Facebook登录设置继续

标题:从Facebook登录设置继续

If you are using Facebook 登录设置 to plan authentication and account flows, connect it with 使用 @capgo/capacitor-social-login for the native capability in 使用 @capgo/capacitor-social-login, @capgo/capacitor-social-login for the implementation detail in @capgo/capacitor-social-login, @capgo/capacitor-passkey for the implementation detail in @capgo/capacitor-passkey, @capgo/capacitor-native-biometric for the implementation detail in @capgo/capacitor-native-biometric, and 两因素身份验证 为两因素认证的实现细节。