react-facebook-login の モバイルWeb 対応について

2020年10月24日 engineering

こんにちは、 @kz_morita です。

今回は,React で Webサービスを構築している際に,Facebook ログインを実装しようとしてちょっと躓いたことをまとめます.

環境と対象のコード

今回のシステムが,Web-Server と API-Server が別れており,Web-Server では主に,ページのレンダリングやリダイレクト周りの処理を行い,提供するコンテンツに関しては,API-Server から取得するといった構成になっていました.

graph LR; Browser-->WebServer; WebServer-->APIServer; Browser -->APIServer;

facebook login には, react-facebook-login を利用していました.

import React from 'react';

// 独自のデザインのFacebookLogin ボタンを用意するため,こちらを import
import FacebookLogin from 'react-facebook-login/dist/facebook-login-render-props';

export const LoginPage: React.FC = () => {

  const handleAuthWithFacebook = React.useCallback(
    (response: FacebookAuthResponse): void => {
      if (response == null || response.accessToken == null) return;
      
      // Facebook の accessToken つかって,signin する処理
      signinWithFacebook(response.accessToken, redirecdtTo);
    },
    [redirectTo]
  );

  return (
    <FacebookLogin
      appId={process.env.FACEBOOK_APP_ID ?? ''}
      callback={onAuthWithFacebook}
      redirectUri={URL.LOGIN_PAGE}
      render={(renderProps: any): React.ReactNode => (
        <button onClick={renderProps.onClick}>Facebook でログイン</Button>
      )}
    />
  );
}

起こったこと

発生した困ったことを時系列でまとめてみます.

モバイル Web で Facebook ログインができない

上記に書いたコードで試したところ,モバイルのときに handleAuthWithFacebook が呼ばれずに,何も起きないような現象になりました.

react-facebook-login のコードを見たところ,isMobile かどうかで,処理を分岐しているようでした.

https://github.com/keppelen/react-facebook-login/blob/master/src/facebook.js#L192

    if (this.props.isMobile && !disableMobileRedirect) {
      window.location.href = `https://www.facebook.com/dialog/oauth${getParamsFromObject(params)}`;
    } else {
      if (!window.FB) {
        if (this.props.onFailure) {
          this.props.onFailure({ status: 'facebookNotLoaded' });
        }

        return;
      }

      window.FB.getLoginStatus(response => {
        if (response.status === 'connected') {
          this.checkLoginState(response);
        } else {
          window.FB.login(this.checkLoginState, { scope, return_scopes: returnScopes, auth_type: params.auth_type });
        }
      });
    }

isMobile = true のときは,https://www.facebook.com/dialog/oauth へと遷移し,パラメータの redirectUri に戻ってくるといった具合でした.

そのため,handleAuthWithFacebook が呼ばれずに,何も起こっていませんでした.

そこで上記の,disableMobileRedirect props を true にする対応を行ったところ,iPhone, Android でもログイン処理を行うことが出来ました.

  <FacebookLogin
    appId={process.env.FACEBOOK_APP_ID ?? ''}
    callback={onAuthWithFacebook}
    redirectUri={URL.LOGIN_PAGE}
    disableMobileRedirect={true}
    render={(renderProps: any): React.ReactNode => (
      <button onClick={renderProps.onClick}>Facebook でログイン</Button>
    )}
  />

Android , iOS の WebView で開くと Facebook ログインができない

今回作成していたサービスの仕様上,ほかアプリから WebView で開かれることも想定されるものだったので,WebView で開けないのは修正が必要です.

原因としては,disableMobileRedirect を true にしたことで,PC 版と同じ JavaScript の Facebook SDK を用いた Dialog を開いて認証する方式になっていたのですが,どうやらその方式と WebView の相性が悪かったらしくうまく動作していませんでした.

解決策として,Mobile の場合は,JavaScript の SDK を用いずに,自前で Facebook のログインフローを構築しました. WebServer は,Node.js (Express) で動いていたので, passport-facebook を用いてログインフローを行いました.

{isMobile || isTablet ? (
  <a href="/auth/facebook/signin">Facebok ログイン</a>
) : (
  <FacebookLogin
    appId={process.env.FACEBOOK_APP_ID ?? ''}
    callback={onAuthWithFacebook}
    isMobile={false}
    render={(renderProps: any): React.ReactNode => (
      <button onClick={renderProps.onClick}>Facebook でログイン</Button>
    )}
  />
)}

ここでは,/auth/facebook/signin が Express で要している Facebook 用のエンドポイントになります.

まとめ

今回は,Facebook ログインを実装するときに躓いたところをまとめました.

モバイルや WebView での動作がうまく行かないケースは結構ありそうなので,要確認だと再認識しました. また,困ったときには ソースコードをちゃんと読みに行くのが大事だなぁと痛感しました.

この記事をシェア