React Native Tech Blog

supported by maricuru (旧maricuru tech blogです)

React Native + Firebaseのログイン機能の実装(メールアドレスと電話番号)

どうも、サトウダイキです。

今日はReact NativeでのFirebaseのログインの実装について書いていきます。4つの方法を実装していきます。

  • メールアドレス
  • 電話番号
  • Facebookアカウント
  • Googleアカウント

この記事ではメールアドレスと電話番号について書きます。続きはこちらで。

https://blog.hatena.ne.jp/ducklingsinc/ducklings.hateblo.jp/edit

tech.maricuru.com

※Expoを利用しています。Versionは36です。2020年2月の時の実装です。

メールアドレス

まずはメールアドレスとパスワードのログインから実装していきます。

Firebaseの設定

Firebaseのプロジェクを作成します。

下記URLからFirebaseプロジェクトを作成します。
ログイン - Google アカウント

f:id:daiki-sato:20200203055201p:plain
Fiebaseプロジェクトの作成

画面の指示通り進めていけば作成できます。

f:id:daiki-sato:20200203061424p:plain
完了後の画面

f:id:daiki-sato:20200203061522p:plain
ログイン方法の設定

[Authentication]→[ログイン方法]→[メール / パスワード] の順でクリック。 有効にするにタブを移動します。メールリンク(パスワードなしでログイン)も有効にしてもOKです。

続いて、アプリの作成を行います。

[Project Overview] → [アプリを追加] → [Web]をクリック

f:id:daiki-sato:20200203070637p:plain
Webアプリの登録

アプリ名を入力して「アプリを登録」をクリック

f:id:daiki-sato:20200203070952p:plain
config画面

firebaseConfigを値をコピーしておきます。これでWebブラウザでのFirebase側の設定は終わりです。

コードの実装

ここからはコーディングです。

ターミナルを立ち上げて、firebaseをinstallします。

色々な記事を読むと、react-native-firebaseを使っているコードと、firebaseを使っているコードがあります。どちらでも実装できますが、今回はfirebaseを使います。

react-native-firebase - npm
firebase - npm

yarn add firebase

configファイルの実装

次にconfigファイルを作成します。

src/configs/firebase.js

import firebase from 'firebase';

export const firebaseConfig = {
  apiKey: '----',
  authDomain: ----',
  databaseURL: ----',
  projectId: ----',
  storageBucket: ----',
  messagingSenderId: ----',
  appId: ----',
  measurementId: ----',
};

export default firebase;

※ ----の部分はfirebaseは自身のfirebaseアプリの設定をしてください。 先ほどコピーしたfirebaseConfigの値を貼り付けます。

ロジックの実装

ロジック部分のコードです。

src/utils/auth.ts

import firebase from '../configs/firebase'; 

// メールアドレスでユーザ登録
export const emailSignUp = (email: string, password: string): void => {
  firebase
    .auth()
    .createUserWithEmailAndPassword(email, password)
    .then(user => {
      if (user) {
        console.log('Success to Signup');
      }
    })
    .catch(error => {
      console.log(error);
    });
};

export default firebase;  

firebaseが予め用意している、createUserWithEmailAndPasswordにemailとpasswordを渡すだけです。

UIの実装

src/utils/auth.ts

import React, { useState } from 'react';
import { View, StyleSheet, TextInput, Button } from 'react-native';
import {
  emailSignUp,
} from '../utils/auth';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    padding: 16,
  },
  textInput: {
    fontSize: 14,
    color: 'black',
    width: '100%',
    borderBottomWidth: 1,
    borderBottomColor: 'black',
    margin: 16,
  },
});

const SignUpScreen: React.FC = (): JSX.Element => {
  const [email, setEmail] = useState();
  const [password, setPassword] = useState();
  
  return (
    <View style={styles.container}>
      <TextInput
        style={styles.textInput}
        value={email}
        onChangeText={(text: string): void => setEmail(text)}
        editable
        maxLength={50}
        placeholder="Email"
        autoCapitalize="none"
        keyboardType="email-address"
        returnKeyType="done"
      />
      <TextInput
        style={styles.textInput}
        value={password}
        onChangeText={(text: string): void => setPassword(text)}
        editable
        maxLength={20}
        placeholder="Password"
        autoCapitalize="none"
        secureTextEntry
        returnKeyType="done"
      />
      <Button title="登録" onPress={(): void => emailSignUp(email, password)} />
    </View>
  );
};

export default SignUpScreen;

特別、難しい処理は何もしていないです。これで終わりです。

確認

f:id:daiki-sato:20200203073856p:plain
アカウント作成画面

シミュレータを立ち上げ、メールアドレスとパスワードを入力して、登録ボタンを押します。

f:id:daiki-sato:20200203074032p:plain
firebaseで確認

正しく設定されると、firebaseにユーザが登録されています。これでメールアドレスでの登録は完了です。

電話番号登録

続いて電話番号を使ったログイン方法についてです。

Webブラウザのfirebaseに移動して、Authenticationから電話番号を有効にします。

f:id:daiki-sato:20200205061255p:plain
電話番号ログインの設定

テスト用の電話番号はまだ登録しないで大丈夫です。これは、1度成功した後にログインの動作を省くために使います。

承認済みドメインの追加

そのまま画面を下にスクロールしていき、[認証済ドメイン]の設定を行います。これ設定忘れるとハマりますので、注意してください。設定する値は、IPアドレスです。

f:id:daiki-sato:20200208214641p:plain
ipアドレスの追加

reCAPTYACの設定

電話番号のログインの実装にはreCAPTYACが必要です。これはfirebaseへの不正のログインを防ぐためです。

reCAPTYACとは、「私はロボットではありません」といったあれです。

f:id:daiki-sato:20200208222316p:plain
reCAPTYAC

ただ、2020年2月時点では、React NativeとFirebaseでのreCAPTYACをうまい具合にやってくれるものが用意されていないので、自分で実装するしかありません。ということで、実際に作っていきます。

まずはターミナルを立ち上げfirebase-toolsをインストールします。

npm install -g firebase-tools  
firebase login  

続いて、実装中のアプリのカレントディレクトに移動して、下記を実行。

firebase init   

f:id:daiki-sato:20200205062558p:plain
firebase initの後の動作

initiを押した後、上下方向キーを使い、hostingを選択し、スペースを押下

? What do you want to use as your public directory? (public)  と聞かれたので、(これは皆さんの好きな場所でokです)

? Which project do you want to add? 対処のプロジェクト

? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) これを一旦y

すると下記が自動で生成されます。

  • firebase.json
  • .firebaserc
  • public/index.html

captchaの実装

public/captcha.htmlを新規作成します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Welcome to Firebase Hosting</title>

  <!-- update the version number as needed -->
  <script defer src="/__/firebase/7.8.0/firebase-app.js"></script>
  <!-- include only the Firebase features as you need -->
  <script defer src="/__/firebase/7.8.0/firebase-auth.js"></script>
  <script defer src="/__/firebase/7.8.0/firebase-database.js"></script>
  <script defer src="/__/firebase/7.8.0/firebase-messaging.js"></script>
  <script defer src="/__/firebase/7.8.0/firebase-storage.js"></script>
  <!-- initialize the SDK after all desired features are loaded -->
  <script defer src="/__/firebase/init.js"></script>

  <style media="screen">
    body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }
  </style>
</head>
<body>
<p>Please, enter captcha for continue<p/>
<button id="continue-btn" style="display:none">Continue to app</button>
<script>
  function getToken(callback) {
    const containerId = 'captcha';
    const container = document.createElement('div');
    container.id = containerId;
    document.body.appendChild(container);
    var captcha = new firebase.auth.RecaptchaVerifier(containerId, {
      'size': 'normal',
      'callback': function(token) {
        callback(token);
      },
      'expired-callback': function() {
        callback('');
      }
    });
    captcha.render().then(function() {
      captcha.verify();
    });
  }

  function sendTokenToApp(token) {
    const baseUri = decodeURIComponent(location.search.replace(/^\?appurl\=/, ''));
    const finalUrl = location.href = baseUri + '/?token=' + encodeURIComponent(token);
    const continueBtn = document.querySelector('#continue-btn');
    continueBtn.onclick = function() {
      window.open(finalUrl, '_blank');
    };
    continueBtn.style.display = 'block';
  }

  document.addEventListener('DOMContentLoaded', function() {
    getToken(sendTokenToApp);
  });
</script>
</body>
</html>  

よりfirebaseのバージョン部分はindex.htmlと同一のを使ってください。

ターミナルを立ち上げてデプロイします。

firebase deploy

成功するとfirebaseのコンソールでこんな感じになります。

f:id:daiki-sato:20200205064136p:plain
firebasehosting成功

パッケージのインストール

さて、ここからは実装に入ります。まずは必要なものをインストールします。

yarn add expo
yarn add expo-web-browser

ロジックの実装

auth.tsに電話番号ログイン処理を追記します。

import { Linking } from 'expo';
import * as WebBrowser from 'expo-web-browser';

// 電話番号で登録
export const phoneSignUp = async (
  phoneNumber: string
): Promise<firebase.auth.ConfirmationResult | null> => {
  const captchaUrl = `https://APPNAME.firebaseapp.com/captcha.html?appurl=${Linking.makeUrl(
    ''
  )}`;

  let token: string | null = null;
  const listener = ({ url }): void => {
    WebBrowser.dismissBrowser();
    const tokenEncoded = Linking.parse(url).queryParams.token;
    if (tokenEncoded) token = decodeURIComponent(tokenEncoded);
  };

  Linking.addEventListener('url', listener);
  await WebBrowser.openBrowserAsync(captchaUrl);
  Linking.removeEventListener('url', listener);
  if (token) {
    // fake firebase.auth.ApplicationVerifier
    const captchaVerifier = {
      type: 'recaptcha',
      verify: () => Promise.resolve(token),
    };
    try {
      const confirmationResult = await firebase
        .auth()
        .signInWithPhoneNumber(phoneNumber, captchaVerifier);
      return confirmationResult;
    } catch (e) {
      console.warn(e);
      return null;
    }
  }
  return null;
};

※APPNAMEの箇所は書き換えが必要です。

UIの実装

SignUpScreen.tsxを追記します。

import {
  emailSignUp,
  *phoneSignUp*
} from '../utils/auth';

---中略---  

 const [phoneNumber, setPhoneNumber] = useState('');
 const [confirmResult, setConfirmResult] = useState(null);
 const onPressPhoneSignUp = async (): Promise<void> => {
    const result = await phoneSignUp(phoneNumber);
    if (result) {
      setConfirmResult(result);
      navigation.navigate('VerificationCode', { confirmResult: result });
    } else {
      console.log('error');
    }
  };

---中略---  
    <TextInput
        style={styles.textInput}
        value={phoneNumber}
        onChangeText={(text: string): void => setPhoneNumber(text)}
        editable
        maxLength={50}
        placeholder="Phone"
        autoCapitalize="none"
        keyboardType="phone-pad"
        returnKeyType="done"
      />
      <Button title="PhoneSignUp" onPress={onPressPhoneSignUp} /></b>

VerificationCodeScreen

import React, { useState } from 'react';
import { View, Text, StyleSheet, TextInput, Button } from 'react-native';
import { NavigationStackProp } from 'react-navigation-stack';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    padding: 16,
  },
  textInput: {
    fontSize: 14,
    color: 'black',
    width: '100%',
    borderBottomWidth: 1,
    borderBottomColor: 'black',
    margin: 16,
  },
});

const VerificationCodeScreen: React.FC<{ navigation: NavigationStackProp }> = ({
  navigation,
}): JSX.Element => {
  const [code, setCode] = useState('');

  const onPressConfirmCode = (): void => {
    if (code.length) {
      const { params = {} } = navigation.state;

      params.confirmResult
        .confirm(code)
        .then(user => {
          console.log('Code Confirmed!');
        })
        .catch(error => console.log(`Code Confirm Error: ${error.message}`));
    }
  };

  return (
    <View style={styles.container}>
      <Text>Enter verification code below:</Text>
      <TextInput
        style={styles.textInput}
        value={code}
        onChangeText={(value): void => setCode(value)}
        editable
        maxLength={50}
        placeholder="Code"
        autoCapitalize="none"
        keyboardType="numeric"
        returnKeyType="done"
      />
      <Button
        title="Confirm Code"
        color="#841584"
        onPress={onPressConfirmCode}
      />
    </View>
  );
};

export default VerificationCodeScreen;

電話番号を入力して、登録を押す→入力した電話番号に確認コードが飛ぶ。確認コードを入力。こういった流れになります。

電話番号は国際コードから入れなければいけません。また先頭の0も消します。080-1234-5678だとしたら、+818012345678と入力します。

今回はここまで。次回はFacebookログインと、Googleログインについて書いていきます。

続きはこちら

https://tech.maricuru.com/entry/2020/02/09/212813 tech.maricuru.com