🏋️
expoでFirebaseRecaptchaを使って電話番号認証する
公式のサンプル動かなかったので動いたコード置いときます
- ページ間の値の受け渡しはReact Navigationでやってますがよしなに
- UIライブラリはNativeBase使ってますがよしなに
電話番号入力ページ
- 電話番号のバリデーションはlibphonenumber-js使いました
import React, { useState } from 'react'
import { useNavigation } from '@react-navigation/native'
import {
Button,
Text,
Form,
Item,
Label,
Input,
Card,
} from 'native-base'
import parsePhoneNumber from 'libphonenumber-js/mobile'
const PhoneNumber: React.FC = () => {
const navigation = useNavigation()
const [phoneNumber, setPhoneNumber] = useState('+81')
const [errorMessage, setErrorMessage] = useState<string | null>()
return (
<>
<Card>
<Form>
<Item last error={!!errorMessage}>
<Label>電話番号</Label>
<Input
autoFocus
autoCompleteType={'tel'}
keyboardType={'numeric'}
value={phoneNumber}
onChangeText={(text) => {
setPhoneNumber(text)
}}
maxLength={13}
/>
</Item>
</Form>
</Card>
{errorMessage && <Text>{errorMessage}</Text>}
<Card>
<Button
block
onPress={() => {
const _phoneNumber = parsePhoneNumber(phoneNumber, 'JP')
if (_phoneNumber?.isValid()) {
navigation.navigate('Recaptcha', {
phoneNumber: phoneNumber,
})
setErrorMessage(null)
} else {
setErrorMessage('電話番号を正しく入力してください')
}
}}
>
<Text>次へ</Text>
</Button>
</Card>
</>
)
}
export default PhoneNumber
Recaptchaを表示するページ
import React, { useEffect } from 'react'
import { useNavigation } from '@react-navigation/native'
import { useRoute, RouteProp } from '@react-navigation/native'
import {
FirebaseRecaptcha,
FirebaseRecaptchaVerifier,
} from 'expo-firebase-recaptcha'
import * as firebase from 'firebase'
import { firebaseConfig } from '../FirebaseConnection'
type Params = {
phoneNumber: { phoneNumber: string }
}
const Recaptcha: React.FC = () => {
const phoneNumberRoute = useRoute<RouteProp<Params, 'phoneNumber'>>()
const phoneNumber = phoneNumberRoute.params.phoneNumber
const [recaptchaToken, setRecaptchaToken] = React.useState('')
const navigation = useNavigation()
useEffect(() => {
let unmounted = false
const f = async () => {
if (phoneNumber && recaptchaToken) {
const applicationVerifier = new FirebaseRecaptchaVerifier(
recaptchaToken
)
const phoneProvider = new firebase.auth.PhoneAuthProvider()
const verificationId = await phoneProvider.verifyPhoneNumber(
phoneNumber,
applicationVerifier
)
navigation.navigate('VerificationCode', {
verificationId: verificationId,
})
}
}
f()
const cleanup = () => {
unmounted = true
}
return cleanup()
}, [phoneNumber, recaptchaToken])
return (
<FirebaseRecaptcha
firebaseConfig={firebaseConfig}
onVerify={(token) => {
setRecaptchaToken(token)
}}
/>
)
}
export default Recaptcha
確認コードを入力するページ
import React, { useState } from 'react'
import { useRoute, RouteProp } from '@react-navigation/native'
import { Button, Text, Form, Item, Label, Input, Card } from 'native-base'
import * as firebase from 'firebase'
type Params = {
verificationId: { verificationId: string }
}
const VerificationCode: React.FC = () => {
const phoneNumberRoute = useRoute<RouteProp<Params, 'verificationId'>>()
const verificationId = phoneNumberRoute.params.verificationId
const [verificationCode, setConfirmationCode] = useState('')
const [errorMessage, setErrorMessage] = useState<string | null>()
const logIn = async () => {
if (!verificationCode) {
setErrorMessage('コードを正しく入力してください')
return
}
const credential = firebase.auth.PhoneAuthProvider.credential(
verificationId,
verificationCode
)
try {
await firebase.auth().signInWithCredential(credential)
setErrorMessage(null)
} catch (error) {
console.log(error)
setErrorMessage('コードを正しく入力してください')
}
}
return (
<>
<Card>
<Form>
<Item last error={!!errorMessage}>
<Label>確認コード</Label>
<Input
autoFocus
keyboardType={'numeric'}
value={verificationCode}
onChangeText={(text) => {
setConfirmationCode(text)
}}
maxLength={6}
/>
</Item>
</Form>
</Card>
{errorMessage && <Text>{errorMessage}</Text>}
<Card>
<Button block onPress={() => logIn()}>
<Text>ログイン</Text>
</Button>
</Card>
</>
)
}
export default VerificationCode
Discussion