You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase中Apple登录账号无法与其他提供商关联的问题咨询

Apple隐藏邮箱导致Firebase账号关联失败问题

主登录界面支持多提供商登录,选择对应提供商后会触发signInWithCredential函数,尝试完成登录并按需关联账号。但Apple允许用户隐藏邮箱以保护隐私,这会导致Firebase无法识别用户,进而无法将该账号与其他提供商账号关联——即便使用JWT的sub令牌作为唯一用户标识也无法解决此问题。当用户退出后使用其他提供商重新登录时,Firebase会分配新的用户ID,此时无法从同一数据库节点获取Apple用户ID,而原始用户ID在用户首次登录时已存储在数据库中。

相关实现代码

/// 异步使用给定凭证登录用户
/// - 参数:
///   - credential: 用于登录用户的认证凭证
///   - appleJWTToken: Apple登录的可选JWT令牌(默认值为nil)
/// - 抛出: 执行过程中出现异常时会抛出`NSError`类型的错误
/// - 返回值: 无返回值

func signInWithCredential(credential: AuthCredential, appleJWTToken: String? = nil) async throws {
    do {
        // 使用提供的凭证尝试用户认证
        let authResult = try await Auth.auth().signIn(with: credential)
        let currentUser = authResult.user

        // 验证认证凭证的提供商
        if credential.provider == FirebaseProviders.Apple.rawValue, let appleJWTToken = appleJWTToken {
            // 如果提供商是Apple,将JWT令牌存储到数据库中
            try await storeJWTToken(appleJWTToken, forUser: currentUser.uid)
        } else if let pendingSignInData = pendingSignInData {
            // 如果存在待处理的凭证,将其与当前账号关联
            try await linkFirebaseAccount(with: pendingSignInData.credential)
        } else {
            // 处理其他提供商的登录,必要时关联Apple账号
            let isAlreadyLinked = isLinkedWithApple(forUser: currentUser.uid)
            let appleJWT = try await retrieveAppleJWT(forUser: currentUser.uid)
            if let appleJWT = appleJWT, !isAlreadyLinked {
                // 如果Apple JWT存在且用户尚未关联Apple账号,创建Apple凭证并关联账号
                let appleCredential = OAuthProvider.credential(withProviderID: FirebaseProviders.Apple.rawValue, idToken: appleJWT, rawNonce: nil)
                try await linkFirebaseAccount(with: appleCredential)
            }
            
            // 关联其他提供商账号,例如Google与Facebook
            try await linkFirebaseAccount(with: credential)
        }
    } catch let error as NSError {
        // 处理账号冲突或登录失败相关错误
        if error.code == AuthErrorCode.accountExistsWithDifferentCredential.rawValue {
            let email = error.userInfo[AuthErrorUserInfoEmailKey] as! String
            let pendingCredential = error.userInfo[AuthErrorUserInfoUpdatedCredentialKey] as! AuthCredential
            guard let providers = try? await Auth.auth().fetchSignInMethods(forEmail: email), let provider = providers.first else {
                throw SignInError.providerNotFound
            }
            pendingSignInData = PendingSignInData(email: email, credential: pendingCredential, method: provider)
            throw SignInError.signInConflict
        } else {
            throw SignInError.failedToSignIn(error)
        }
    }
}

fileprivate func linkFirebaseAccount(with credential: AuthCredential) async throws {
    guard let currentUser = Auth.auth().currentUser else {
        // 处理错误
        throw SignInError.failedToLinkAccount
    }
    
    do {
        try await currentUser.link(with: credential)
        pendingSignInData = nil  // 关联成功后清除待处理数据
    } catch {
        // 处理错误
        throw SignInError.failedToLinkAccount
    }
}

内容的提问来源于stack exchange,提问作者David.C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 06:02:17