ios - Firebase UI电子邮件链接打开应用程序,并通过适当的方法处理,但未登录用户

标签 ios swift firebase firebase-authentication firebaseui

iOS目标:12.2
语言:斯威夫特
Firebase SDK版本:6.3.0
Firebase UI版本:8.1.0
Firebase动态链接版本:4.0.5
设备:运行iOS 12.4.2的iPad Air


我目前在iOS(swift)上使用FirebaseUI遇到问题,当我尝试处理电子邮件链接时,它将无法登录用户。

我让用户使用FirebaseUI电子邮件链接流,该流成功将电子邮件发送到他们的电子邮件。当我打开电子邮件中的链接时,它会通过方法打开

AppDelegate.swift

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    print(self.TAG, "GOT TO RESTORATION HANDLER!", userActivity, userActivity.webpageURL)
    guard let webUrl = userActivity.webpageURL else {
        return false
    }
    let handled = DynamicLinks.dynamicLinks().handleUniversalLink(webUrl) { (link, err) in
        print(self.TAG, "HANDLING UNIVERSAL LINK!", link, err)
    self.handlePasswordlessSignIn(with: link?.url)
    }
    return handled
}


这是根据https://firebase.google.com/docs/dynamic-links/ios/receive#open-dynamic-links-in-your-app中的步骤6完成的。

然后,我尝试像这样处理登录:

if let link = link, let auth = FUIAuth.defaultAuthUI(), let del = delegate {
    let actionCodeSettings = ActionCodeSettings()
        actionCodeSettings.url = URL(string: websiteContinueURL )
    actionCodeSettings.handleCodeInApp = true
    actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
    actionCodeSettings.setAndroidPackageName(bundleIdentifier, installIfNotAvailable: true, minimumVersion: "12")
    let provider = FUIEmailAuth(authAuthUI: auth, signInMethod: EmailLinkAuthSignInMethod, forceSameDevice: true, allowNewEmailAccounts: true, actionCodeSetting: actionCodeSettings)

    auth.providers = [provider, FUIGoogleAuth(), FUIFacebookAuth()]
    print(self.TAG, "TRYING TO SIGN IN EMAIL LINK!!!")

    if auth.handleOpen(link, sourceApplication: Bundle.main.bundleIdentifier!) {
        // if it handled it, which it does, I callback from here
        // but the auth state at this point, or at any other point after this,
        // even after the "signed in" modal, is unchanged
    }
}


然后提供一个模式弹出窗口,显示“已登录!”。但是用户的身份验证状态不变。我使用该方法来处理网址,具体是因为
a)由于使用FirebaseUI,在他们登录之前,我无法访问他们用于注册的用户的电子邮件
和b)是通过文档https://firebase.google.com/docs/auth/ios/firebaseui#email_link_authentication告知我使用的方法(这是Auth.defaultAuthUI,但应为FUIAuth.defaultAuthUI(),Auth没有属性/方法defaultAuthUI,因为这是FirebaseUI组件)

我尝试了以下方法:


在打开应用程序时将链接发送到的应用程序方法中,我尝试发送原始的userActivity.webpageURL。不起作用。
在此之前,我尝试过注销用户,以便在处理url之前没有现有用户。不起作用。
尝试捕获其他应用程序方法中的url,该方法不起作用,因为它只能通过还原处理程序发送到该方法。


这仅在iOS上发生,因为我每次都能完成android中的相同流程而不会出现问题。由于电子邮件链接验证似乎要求将应用程序部署到应用程序商店,因此我也无法在自己的项目中复制此文件,因为我以前遇到的一个早期问题是动态链接不会打开该应用程序。但是,一旦解决了该问题,就会弹出该对话框。

注意:我已经将登录方法追溯到我可以看到的代码中,并且sourceApplication字段似乎无关紧要,因此即使我正在使用包标识符,接收它的方法也永远不会使用它。只是为了确认:

FUI验证

- (BOOL)handleOpenURL:(NSURL *)URL
    sourceApplication:(NSString *)sourceApplication {
  // Complete IDP-based sign-in flow.
  for (id<FUIAuthProvider> provider in _providers) {
    if ([provider handleOpenURL:URL sourceApplication:sourceApplication]) {
      return YES;
    }
  }
  // The URL was not meant for us.
  return NO;
}


FUIEmailAuth.m(在其中实现的类扩展了FUIAuthProvider协议)

- (BOOL)handleOpenURL:(NSURL *)URL sourceApplication:(nullable NSString *)sourceApplication {
  self.emailLink = URL.absoluteString;

  // Retrieve continueUrl from URL
  NSURLComponents *urlComponents = [NSURLComponents componentsWithString:URL.absoluteString];
  NSString *continueURLString;
  for (NSURLQueryItem *queryItem in urlComponents.queryItems) {
    if ([queryItem.name isEqualToString:@"continueUrl"]) {
      continueURLString = queryItem.value;
    }
  }
  if (!continueURLString) {
    NSLog(@"FUIEmailAuth unable to handle url without continue URL: %@", URL);
    return NO;
  }

  // Retrieve url parameters from continueUrl
  NSMutableDictionary *urlParameterDict= [NSMutableDictionary dictionary];
  NSURLComponents *continueURLComponents = [NSURLComponents componentsWithString:continueURLString];
  for (NSURLQueryItem *queryItem in continueURLComponents.queryItems) {
    urlParameterDict[queryItem.name] = queryItem.value;
  }
  // Retrieve parameters from local storage
  NSMutableDictionary *localParameterDict = [NSMutableDictionary dictionary];
  localParameterDict[kEmailLinkSignInEmailKey] = [GULUserDefaults.standardUserDefaults
                                                  stringForKey:kEmailLinkSignInEmailKey];
  localParameterDict[@"ui_sid"] = [GULUserDefaults.standardUserDefaults stringForKey:@"ui_sid"];

  // Handling flows
  NSString *urlSessionID = urlParameterDict[@"ui_sid"];
  NSString *localSessionID = localParameterDict[@"ui_sid"];
  BOOL sameDevice = urlSessionID && localSessionID && [urlSessionID isEqualToString:localSessionID];

  if (sameDevice) {
    // Same device
    if (urlParameterDict[@"ui_pid"]) {
      // Unverified provider linking
      [self handleUnverifiedProviderLinking:urlParameterDict[@"ui_pid"]
                                      email:localParameterDict[kEmailLinkSignInEmailKey]];
    } else if (urlParameterDict[@"ui_auid"]) {
      // Anonymous upgrade
      [self handleAnonymousUpgrade:urlParameterDict[@"ui_auid"]
                             email:localParameterDict[kEmailLinkSignInEmailKey]];
    } else {
      // Normal email link sign in
      [self handleEmaiLinkSignIn:localParameterDict[kEmailLinkSignInEmailKey]];
    }
  } else {
    // Different device
    if ([urlParameterDict[@"ui_sd"] isEqualToString:@"1"]) {
      // Force same device enabled
      [self handleDifferentDevice];
    } else {
      // Force same device not enabled
      [self handleConfirmEmail];
    }
  }

  return YES;
}


所以我不认为这是问题。

最佳答案

更新

在iOS上
答案是尝试从FirebaseUI提交初始电子邮件注册/流入之前尝试注销用户,以便发送到其电子邮件的链接不包含匿名UID字段

(请参阅下面的详细信息,了解这样做的原因(至少对我而言))



一些细节:

从昨天开始,我一直在不停地调试此工具。因此,对于遇到此非常具体问题的任何人,我想进一步了解一下上面的上下文:


如果我从Firebase删除帐户,则身份验证实际上可以正常工作,但仅在首次登录/注册时才有效。
通常,在用户注册之前,我总是让用户以匿名身份登录(在这种情况下,我让FirebaseUI处理自动升级或常规登录)。


输入的问题是,我根据(2)做出假设,即FirebaseUI将为所有身份验证方法正确处理这种情况。对于电子邮件链接身份验证,当用户最初将其电子邮件输入FirebaseUI流时,如果当时他们是匿名用户,它将发送带有指示其匿名用户ID的字段/参数的电子邮件链接。如果通过动态链接再次打开应用程序时此字段存在,则该应用程序收到的链接将发送到此方法:

FUIAuth.defaultAuthUI()!.handleOpen(link, sourceApplication: bundleIdentifier)


将会发生的是它将尝试将匿名用户自动升级到电子邮件帐户。

现在,第一次尝试创建新帐户,就可以正常工作。但是,为了以后尝试使用firebase ui流对同一封电子邮件进行身份验证,它将尝试将匿名用户自动升级到已经存在的帐户或类似的帐户(具体信息可能会决定这是否实际上是FirebaseUI问题, ,或者如果是FirebaseAuth问题,或者是否按预期运行等)。

显然,如果您依赖于附加到特定UID的数据,则此方法将对您不起作用,因为如果您从匿名用户注销然后使用电子邮件帐户创建/登录,则UID永远不会保留在电子邮件身份验证中。

如果您需要在Auth之前和之后保留UID,或者您不能无问题地退出用户,并且必须使用FirebaseUI,那么我建议您在用户在您的应用程序内进行身份验证后,阻止其退出。这不是一个很好的解决方案,因此删除电子邮件身份验证作为iOS上FirebaseUI的提供程序并依赖其他身份验证提供程序可能是更好的临时解决方案。

以上都是临时解决方案

FirebaseUI团队应该已经通过https://github.com/firebase/FirebaseUI-iOS/issues/741意识到了这个问题(我发现这实际上正是我所面临的问题,但是我最初忽略了这个问题,因为我根本不认为它是重新认证的)。希望他们能够在此问题的修复方面取得更多进展。如果我对如何最好地解决这个问题有任何想法,我自己做一个PR,但是由于我不这样做,我将仅依靠它并等待。



AutoUpgradeAnonymousUsers = false应该怎么办?

结果是,在查看了FirebaseUI / Auth / FUIAuth.m,FirebaseUI / Email / FUIEmailAuth.m和FirebaseUI / Email / FUIEmailEntryViewController.m的代码之后,在整个过程中将此标志设置为false(注册/登录,发送链接,打开链接,处理链接),则不会更改任何内容。如果有任何问题,将导致整个流程始终失败。由于用户在初始电子邮件链接发送时是匿名用户,因此它仍将在链接中包含匿名uid,这又将导致处理url的方法运行处理升级匿名用户的方法(因为存在一个匿名uid)。似乎“需要”升级)。

FUIEmailAuth.m

- (BOOL)handleOpenURL:(NSURL *)URL sourceApplication:(nullable NSString *)sourceApplication {

...

 } else if (urlParameterDict[@"ui_auid"]) {
   [seld handleAnonymousUpgrade:urlParameterDict[@"ui_auid"] email:localParameterDict[kEmailLinkSignInEmailKey]];
 } else {

...

}


这种处理该方法的方法将在初始检查时跳过,因为我们没有升级匿名用户,该方法然后试图处理这种情况,就好像该用户在另一台设备上打开了链接(至少在这种情况下不正确)。根本就是不允许用户登录。

FUIEmailAuth.m

- (void)handleAnonymousUpgrade:(NSString *)anonymousUserID email:(NSString *)email {
  // Check for the presence of an anonymous user and whether automatic upgrade is enabled.
  if (self.authUI.auth.currentUser.isAnonymous &&
      self.authUI.shouldAutoUpgradeAnonymousUsers &&
      [anonymousUserID isEqualToString:self.authUI.auth.currentUser.uid]) {

  ...

  } else {
    [self handleDifferentDevice];
  }

关于ios - Firebase UI电子邮件链接打开应用程序,并通过适当的方法处理,但未登录用户,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58290744/

相关文章:

ios - Unity Firebase Analytics 事件未显示

angularjs - Firebase snapshot.val() 未绑定(bind)到 $scope

ios - 对于 iOS 和 Swift 中的 firebase 监听器,如何使用带有 queryStartingAtValue 的 firebase 服务器时间戳来跟踪用户的当前操作

ios - 如何处理 iOS 设备锁定或在其他应用程序上播放的背景音频?

ios - 居中没有容器 View 的 View ?

ios - 滚动时 UITableViewCell contenview 上的阴影率增加

swift - Swift 中协议(protocol)的只读属性

swift - 记录游戏得分的代码

ios - 将相机旋转与 SceneView 中的网格/刻度对齐

ios - 将 UIImage 转换为 NSData 并在 Swift 中转换回 UIImage?