0

我正在使用 Firbase 在我的 Ionic 3 应用程序中实现 Google 登录。我已经成功地能够使用本机 Google Plus Cordova 插件,然后使用Angular Fire 2使用凭据登录 Firebase 。这是我的代码:

public loginWithGoogle(): Promise<any> {

    return new Promise((resolve, reject) => {

      let loginPromise: Promise<any>;

      if (this.isMobile()) {
        loginPromise = new Promise((resolve, reject) => {
          this.googleplus.login({
            'webClientId': '--------.apps.googleusercontent.com',
            'offline': true
          })
            .then(res => {
              this.afAUth.auth.signInWithCredential(firebase.auth.GoogleAuthProvider.credential(res.idToken))
                .then(firebaseRes => {
                  resolve(firebaseRes);
                })
                .catch(err => {
                  reject(err);
                });
            })
            .catch(err => {
              reject(err);
            });
        });
      } else {
        loginPromise = this.afAUth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider()) as Promise<any>;
      }

      loginPromise
        .then(res => {
          console.log('login promise done: ' + JSON.stringify(res));

          const user: User = {
            uid: res.user.uid,
            email: res.user.email,
            displayName: res.user.displayName
          };

          resolve(user);
        });
    });
}

在我的登录页面中调用它:

this.auth.loginWithGoogle()
  .then(res => {
    console.log('success login !!!!!');
  })
  .catch(err => {
    console.log('error login !!!!!');
  });

但是,当我使用 Xcode 部署到 iOS 时,我可以看到控制台输出:

2017-06-11 11:32:01.384130 MyApp[2634:842028] login promise done: {"uid":"...","displayName":"...","photoURL":"..."...}

承诺永远不会解决!我曾尝试手动使用 ZoneJS 但未成功:

private zone;

public loginWithGoogle(): Promise<any> {

  this.zone = new NgZone({});

  ...

      this.zone.run(() => {
          resolve(user);
      });
    });
  });
}

但结果是一样的。有关信息,登录在浏览器中工作正常。

4

1 回答 1

1

问题来自访问res.user,仅在使用弹出登录时存在。使用凭据登录时返回的对象在其根目录中包含uid,email和其他用户属性。无论您是在移动环境还是凉亭环境中,您都需要以不同的方式创建用户。在移动设备res.user.uid上,访问和浏览器访问res.uid

恕我直言,这是 Angular Fire 方面的误导性接口规范。

此外,代码很难阅读。Promise 链不是一个好习惯。如果可能,直接使用 Observables,如果不可能,则使用 RxJS 的fromPromise运算符将 Promise 转换为 Observables。然后,您可以将 flatMap 结果链接到异步调用并获得干净的代码。Observables 的一个优点是该subscribe函数的错误回调将捕获链期间发生的任何错误,而对于 Promise,您需要手动捕获和拒绝。

通过一些重构,您会得到以下结果(有效):

private signInWithGooglePlus(): Observable<any> {
  return Observable.fromPromise(
    ...
  );
}

private signInFirebaseWithCredentials(idToken: string): Observable<User> {
  return Observable.fromPromise(
    ...
  ).map(credentials => ({
    uid: credentials.uid,
    ...
  }));
}

private signInWithGooglePopup(): Observable<User> {
  return Observable.fromPromise(
    ...
  ).map(firebaseRes => ({
    uid: firebaseRes.user.uid,
    ...
  }));
}

public loginWithGoogle(): Observable<any> {

  let loginPromise: Observable<User>;

  if (this.isMobile()) {
    loginPromise = this.signInWithGooglePlus().flatMap(res => this.signInFirebaseWithCredentials(res.idToken));
  } else {
    loginPromise = this.signInWithGooglePopup();
  }

  let user: User;

  return loginPromise
    .flatMap(userTmp => {
      user = userTmp;
      return this.getJWTToken(userTmp.uid);
    })
    .map(token => {
      this.storeData(token, user);
      return user;
    });
}
于 2017-06-11T13:22:11.903 回答