React Native
...
Cloud Functions
React Native用Parse SDKでのOTP認証クラウドファンクション実装
9 分
クラウドファンクションを使用したreact nativeのotp認証 はじめに このガイドでは、parse cloud code関数を使用して、react nativeアプリにtwilio verify apiを統合し、otpログイン機能を有効にする方法を学びます。cloud code関数は、この種の操作に最適な場所であり、機密データを扱い、アプリケーションの外部で他のapiを使用することができます。 このガイドでは、otp統合を使用してreact nativeコンポーネントを実装する方法も確認し、以前のガイドのユーザーメール確認の例を改善します。 いつでも、このチュートリアルで構築された完全なandroidプロジェクトに、私たちのgithubリポジトリでアクセスできます kotlinの例リポジトリ javaの例リポジトリ 前提条件 このチュートリアルを完了するには、次のものが必要です: 作成されたreact nativeアプリと back4appに接続された どのように クラウド関数をデプロイするか を理解すること。 twilioのアクティブアカウント、無料トライアルを作成できます こちら sendgridのアクティブアカウント、無料トライアルを作成できます こちら 目標 twilio verify apiをparse cloud code関数を使用してback4appに統合し、react nativeアプリで使用します。 1 twilio verifyの設定 twilioは、現在多くの企業に使用されている有名な2faトークンおよびotpサービスプロバイダーです。verify apiは、開発者が電話、メールを確認し、手間なくパスワードなしでログインを行うのを助けるための簡略化されたapiセットです。このガイドでは、smsおよびメール確認方法を使用して、アプリケーションにotp機能を有効にします。公式のverify ドキュメント https //www twilio com/docs/verify/api を参照しますので、どのステップが混乱した場合は参照してください。 twilioとsendgridのアカウントを作成した後(sendgridはユーザートークンを含む自動メールを送信するために必要です)、各サービスのダッシュボードから取得できる以下の識別子を書き留めてください twilioのアカウントsidと認証トークン; sendgrid idとシンプルなメールテンプレートid。 次に、twilio verifyで新しいverifyサービスを作成します ダッシュボード https //www twilio com/console/verify/dashboard , これはotpリクエストと検証を管理するための設定のセットです。ここにサービスsidを書き留めておくことを忘れないでください。希望する場合は、verifyサービス内で新しいメール統合を作成してsendgridキーを追加することで、メールコードの送信を有効にすることもできます ダッシュボード https //www twilio com/docs/verify/email これで、すべての設定が完了しましたので、cloud code関数を作成することができます。 2 cloud code functionsを介した統合 前述のように、アプリケーションでcloud code関数を使用することで、コードに大きな柔軟性が生まれ、アプリから再利用可能なメソッドを切り離し、その動作をより良く制御することが可能になります。 cloud functionsのスターターガイド https //www back4app com/docs/get started/cloud functions を確認またはレビューできます。 最初のクラウド関数「 requestotp requestotp 」を作成しましょう。この関数では、otpが作成され、選択した方法でユーザーに送信されます。この関数は、 userdata userdata (メールアドレスまたは電話番号を含む)と、 erificationtype erificationtype (どの検証方法を使用するかを指定)を受け取る必要があります。 email email または sms sms を指定します。 こちらが関数のコードです 1 // define twilio keys and require the library helper 2 const accountsid = 'your twilio account sid here'; 3 const authtoken = 'your twilio auth token here'; 4 const client = require('twilio')(accountsid, authtoken); 5	 6 // request otp 7 parse cloud define('requestotp', async (request) => { 8 const userdata = request params userdata; 9 const verificationtype = request params verificationtype; 10 let verification request = await client verify 11 services('your twilio verify service id here') 12 verifications create({ to userdata, channel verificationtype }); 13 return { status verification request status, error '' }; 14 }); 最初に、twilioのapiキーを定義し、ヘルパーライブラリをインポートしたことに注意してください。back4appのparse server実装は、デフォルトでtwilioライブラリへのアクセスを提供するため、サーバーにインストールする必要はありません。 2つ目で最後のクラウド関数は「 verifyotp verifyotp 」と呼ばれ、twilioのverifyでユーザーのトークンを検証し、自動的にログインします。セッションを作成し、そのidをアプリケーションに返すため、そこからパスワードなしでログインできます。必要なパラメータは4つあり、最初の2つは前の関数と同じで、さらに usertoken usertoken (提供された検証トークンを含む)と、さらに userinstallationid userinstallationid (後で説明します)を追加します。 1 // since we need in uuid v4 id for creating a new session, this function 2 // mocks the creation of one without the need to import the `uuidv4` module 3 // for a more robust solution, consider using the uuid module, which uses 4 // higher quality rng apis 5 // adapted from https //stackoverflow\ com/a/2117523 6 function uuidv4() { 7 return 'xxxxxxxx xxxx 4xxx yxxx xxxxxxxxxxxx' replace(/\[xy]/g, function (c) { 8 var r = (math random() 16) | 0, 9 v = c == 'x' ? r (r & 0x3) | 0x8; 10 return v tostring(16); 11 }); 12 } 13	 14 // verify otp 15 parse cloud define('verifyotp', async (request) => { 16 const { userdata, verificationtype, usertoken, userinstallationid } = 17 request params; 18 let verification check = await client verify 19 services('your twilio verify service id here') 20 verificationchecks create({ to userdata, code usertoken }); 21 // status can be 'approved' if correct or 'pending' if incorrect 22 if (verification check status === 'approved') { 23 try { 24 // get user to login 25 let user = null; 26 if (verificationtype === 'sms') { 27 user = await new parse query(parse user) 28 equalto('phonenumber', userdata) 29 first({ usemasterkey true }); 30 } else { 31 user = await new parse query(parse user) 32 equalto('email', userdata) 33 first({ usemasterkey true }); 34 } 35 // create new session for login use in 36 // manually create session (without using parse session because of it's update restrictions) 37 // adapted from https //stackoverflow\ com/a/67432715 38 let session = new parse object(' session', { 39 user user, 40 installationid userinstallationid, 41 sessiontoken `r ${uuidv4()}`, 42 }); 43 session = await session save(undefined, { usemasterkey true }); 44 return { sessionid session get('sessiontoken') }; 45 } catch (error) { 46 console log(error); 47 return { error `${error}` }; 48 } 49 } 50 return { error 'could not validate your token or account! try again!' }; 51 }); 次のステップに進む前に、これらの関数をparseサーバーにデプロイしてください。 3 react nativeでotp機能を作成する ユーザーのメール確認の ガイド https //www back4app com/docs/react native/parse sdk/working with users/react native email verification を基にして、新しいotp機能を有効にするためにいくつかの変更を加えましょう。ガイドを続ける前に、プロジェクトの例をダウンロードして設定することをお勧めします。 まず、ユーザーが電話番号を使用してログインできるようにするために、新しい入力フィールドを追加し、その値を userregistration js userregistration js のユーザー登録保存メソッドに追加します(または userregistration tsx userregistration tsx )ファイル javascript 1 import react, {usestate} from 'react'; 2 import { 3 alert, 4 image, 5 text, 6 textinput, 7 touchableopacity, 8 view, 9 } from 'react native'; 10 import parse from 'parse/react native'; 11 import {usenavigation} from '@react navigation/native'; 12 import {stackactions} from '@react navigation/native'; 13 import styles from ' /styles'; 14	 15 export const userregistration = () => { 16 const navigation = usenavigation(); 17	 18 const \[username, setusername] = usestate(''); 19 const \[password, setpassword] = usestate(''); 20 const \[email, setemail] = usestate(''); 21 const \[phonenumber, setphonenumber] = usestate(''); 22	 23 const dousersignup = async function () { 24 // note that this values come from state variables that we've declared before 25 const usernamevalue = username; 26 const passwordvalue = password; 27 const emailvalue = email; 28 const phonenumbervalue = phonenumber; 29 try { 30 // since the signup method returns a promise, we need to call it using await 31 // note that now you are setting the user email value as well 32 let createduser = await parse user signup(usernamevalue, passwordvalue, { 33 email emailvalue, 34 phonenumber phonenumbervalue, 35 }); 36	 37 // parse user signup returns the already created parseuser object if successful 38 alert alert( 39 'success!', 40 `user ${createduser get( 41 'username', 42 )} was successfully created! verify your email to login`, 43 ); 44 // since email verification is now required, make sure to log out 45 // the new user, so any session created is cleared and the user can 46 // safely log in again after verifying 47 await parse user logout(); 48 // go back to the login page 49 navigation dispatch(stackactions poptotop()); 50 return true; 51 } catch (error) { 52 // signup can fail if any parameter is blank or failed an uniqueness check on the server 53 alert alert('error!', error message); 54 return false; 55 } 56 }; 57	 58 return ( 59 \<view style={styles login wrapper}> 60 \<view style={styles form}> 61 \<textinput 62 style={styles form input} 63 value={username} 64 placeholder={'username'} 65 onchangetext={(text) => setusername(text)} 66 autocapitalize={'none'} 67 keyboardtype={'email address'} 68 /> 69 \<textinput 70 style={styles form input} 71 value={email} 72 placeholder={'email'} 73 onchangetext={(text) => setemail(text)} 74 autocapitalize={'none'} 75 keyboardtype={'email address'} 76 /> 77 \<textinput 78 style={styles form input} 79 value={phonenumber} 80 placeholder={'phone (international format +15017122661)'} 81 onchangetext={(text) => setphonenumber(text)} 82 autocapitalize={'none'} 83 keyboardtype={'phone pad'} 84 /> 85 \<textinput 86 style={styles form input} 87 value={password} 88 placeholder={'password'} 89 securetextentry 90 onchangetext={(text) => setpassword(text)} 91 /> 92 \<touchableopacity onpress={() => dousersignup()}> 93 \<view style={styles button}> 94 \<text style={styles button label}>{'sign up'}\</text> 95 \</view> 96 \</touchableopacity> 97 \</view> 98 \<view style={styles login social}> 99 \<view style={styles login social separator}> 100 \<view style={styles login social separator line} /> 101 \<text style={styles login social separator text}>{'or'}\</text> 102 \<view style={styles login social separator line} /> 103 \</view> 104 \<view style={styles login social buttons}> 105 \<touchableopacity> 106 \<view 107 style={\[ 108 styles login social button, 109 styles login social facebook, 110 ]}> 111 \<image 112 style={styles login social icon} 113 source={require(' /assets/icon facebook png')} 114 /> 115 \</view> 116 \</touchableopacity> 117 \<touchableopacity> 118 \<view style={styles login social button}> 119 \<image 120 style={styles login social icon} 121 source={require(' /assets/icon google png')} 122 /> 123 \</view> 124 \</touchableopacity> 125 \<touchableopacity> 126 \<view style={styles login social button}> 127 \<image 128 style={styles login social icon} 129 source={require(' /assets/icon apple png')} 130 /> 131 \</view> 132 \</touchableopacity> 133 \</view> 134 \</view> 135 <> 136 \<touchableopacity onpress={() => navigation navigate('login')}> 137 \<text style={styles login footer text}> 138 {'already have an account? '} 139 \<text style={styles login footer link}>{'log in'}\</text> 140 \</text> 141 \</touchableopacity> 142 \</> 143 \</view> 144 ); 145 };1 import react, {fc, reactelement, usestate} from 'react'; 2 import { 3 alert, 4 image, 5 text, 6 textinput, 7 touchableopacity, 8 view, 9 } from 'react native'; 10 import parse from 'parse/react native'; 11 import {usenavigation} from '@react navigation/native'; 12 import {stackactions} from '@react navigation/native'; 13 import styles from ' /styles'; 14	 15 export const userregistration fc<{}> = ({}) reactelement => { 16 const navigation = usenavigation(); 17	 18 const \[username, setusername] = usestate(''); 19 const \[password, setpassword] = usestate(''); 20 const \[email, setemail] = usestate(''); 21 const \[phonenumber, setphonenumber] = usestate(''); 22	 23 const dousersignup = async function () promise\<boolean> { 24 // note that this values come from state variables that we've declared before 25 const usernamevalue string = username; 26 const passwordvalue string = password; 27 const emailvalue string = email; 28 const phonenumbervalue string = phonenumber; 29 try { 30 // since the signup method returns a promise, we need to call it using await 31 // note that now you are setting the user email value as well 32 let createduser = await parse user signup(usernamevalue, passwordvalue, { 33 email emailvalue, 34 phonenumber phonenumbervalue, 35 }); 36	 37 // parse user signup returns the already created parseuser object if successful 38 alert alert( 39 'success!', 40 `user ${createduser get( 41 'username', 42 )} was successfully created! verify your email to login`, 43 ); 44 // since email verification is now required, make sure to log out 45 // the new user, so any session created is cleared and the user can 46 // safely log in again after verifying 47 await parse user logout(); 48 // go back to the login page 49 navigation dispatch(stackactions poptotop()); 50 return true; 51 } catch (error object) { 52 // signup can fail if any parameter is blank or failed an uniqueness check on the server 53 alert alert('error!', error message); 54 return false; 55 } 56 }; 57	 58 return ( 59 \<view style={styles login wrapper}> 60 \<view style={styles form}> 61 \<textinput 62 style={styles form input} 63 value={username} 64 placeholder={'username'} 65 onchangetext={(text) => setusername(text)} 66 autocapitalize={'none'} 67 keyboardtype={'email address'} 68 /> 69 \<textinput 70 style={styles form input} 71 value={email} 72 placeholder={'email'} 73 onchangetext={(text) => setemail(text)} 74 autocapitalize={'none'} 75 keyboardtype={'email address'} 76 /> 77 \<textinput 78 style={styles form input} 79 value={phonenumber} 80 placeholder={'phone (international format +15017122661)'} 81 onchangetext={(text) => setphonenumber(text)} 82 autocapitalize={'none'} 83 keyboardtype={'phone pad'} 84 /> 85 \<textinput 86 style={styles form input} 87 value={password} 88 placeholder={'password'} 89 securetextentry 90 onchangetext={(text) => setpassword(text)} 91 /> 92 \<touchableopacity onpress={() => dousersignup()}> 93 \<view style={styles button}> 94 \<text style={styles button label}>{'sign up'}\</text> 95 \</view> 96 \</touchableopacity> 97 \</view> 98 \<view style={styles login social}> 99 \<view style={styles login social separator}> 100 \<view style={styles login social separator line} /> 101 \<text style={styles login social separator text}>{'or'}\</text> 102 \<view style={styles login social separator line} /> 103 \</view> 104 \<view style={styles login social buttons}> 105 \<touchableopacity> 106 \<view 107 style={\[ 108 styles login social button, 109 styles login social facebook, 110 ]}> 111 \<image 112 style={styles login social icon} 113 source={require(' /assets/icon facebook png')} 114 /> 115 \</view> 116 \</touchableopacity> 117 \<touchableopacity> 118 \<view style={styles login social button}> 119 \<image 120 style={styles login social icon} 121 source={require(' /assets/icon google png')} 122 /> 123 \</view> 124 \</touchableopacity> 125 \<touchableopacity> 126 \<view style={styles login social button}> 127 \<image 128 style={styles login social icon} 129 source={require(' /assets/icon apple png')} 130 /> 131 \</view> 132 \</touchableopacity> 133 \</view> 134 \</view> 135 <> 136 \<touchableopacity onpress={() => navigation navigate('login')}> 137 \<text style={styles login footer text}> 138 {'already have an account? '} 139 \<text style={styles login footer link}>{'log in'}\</text> 140 \</text> 141 \</touchableopacity> 142 \</> 143 \</view> 144 ); 145 }; 新しい userotp userotp スクリーンを作成し、すべてのotpプロセスを処理します。このスクリーンには2つの入力フィールドがあり、最初のフィールドはユーザーがotpを取得する手段(メールアドレスまたは電話番号)を提供するためのものです。もう1つの入力フィールドは、otpリクエストを送信する前は非表示になっており、ユーザーが受け取ったトークンを含みます。以下が完全な userotp js userotp js (または userotp tsx userotp tsx ) コードです javascript 1 import react, {usestate} from 'react'; 2 import {alert, text, textinput, touchableopacity, view} from 'react native'; 3 import parse from 'parse/react native'; 4 import {usenavigation} from '@react navigation/native'; 5 import styles from ' /styles'; 6	 7 export const userotp = () => { 8 const navigation = usenavigation(); 9	 10 const \[userdata, setuserdata] = usestate(''); 11 const \[usertoken, setusertoken] = usestate(''); 12 const \[tokenrequested, settokenrequested] = usestate(false); 13	 14 const requestotp = async function () { 15 // note that this values come from state variables that we've declared before 16 const userdatavalue = userdata; 17 // check if value is an email if it contains @ note that in a real 18 // app you need a much better validator for this field 19 const verificationtype = 20 userdatavalue includes('@') === true ? 'email' 'sms'; 21 // we need to call it using await 22 try { 23 await parse cloud run('requestotp', { 24 userdata userdatavalue, 25 verificationtype verificationtype, 26 }); 27 // show token input field 28 settokenrequested(true); 29 alert alert('success!', `token requested via ${verificationtype}!`); 30 return true; 31 } catch (error) { 32 alert alert('error!', error message); 33 return false; 34 } 35 }; 36	 37 const verifyotp = async function () { 38 // note that this values come from state variables that we've declared before 39 const userdatavalue = userdata; 40 const usertokenvalue = usertoken; 41 // check if value is an email if it contains @ note that in a real 42 // app you need a much better validator for this field 43 const verificationtype = 44 userdatavalue includes('@') === true ? 'email' 'sms'; 45 // we need the installation id to allow cloud code to create 46 // a new session and login user without password 47 const parseinstallationid = await parse getinstallationid(); 48 // we need to call it using await 49 try { 50 // verify otp, if successful, returns a sessionid 51 let response = await parse cloud run('verifyotp', { 52 userdata userdatavalue, 53 verificationtype verificationtype, 54 usertoken usertokenvalue, 55 parseinstallationid parseinstallationid, 56 }); 57 if (response sessionid !== undefined) { 58 // use generated sessionid to become a user, 59 // logging in without needing to inform password and username 60 await parse user become(response sessionid); 61 const loggedinuser= await parse user currentasync(); 62 alert alert( 63 'success!', 64 `user ${loggedinuser get('username')} has successfully signed in!`, 65 ); 66 // navigation navigate takes the user to the home screen 67 navigation navigate('home'); 68 return true; 69 } else { 70 throw response; 71 } 72 } catch (error) { 73 alert alert('error!', error message); 74 return false; 75 } 76 }; 77	 78 return ( 79 \<view style={styles login wrapper}> 80 {tokenrequested === false ? ( 81 \<view style={styles form}> 82 \<textinput 83 style={styles form input} 84 value={userdata} 85 placeholder={'email or mobile phone number'} 86 onchangetext={(text) => setuserdata(text)} 87 autocapitalize={'none'} 88 keyboardtype={'email address'} 89 /> 90 \<touchableopacity onpress={() => requestotp()}> 91 \<view style={styles button}> 92 \<text style={styles button label}>{'request otp'}\</text> 93 \</view> 94 \</touchableopacity> 95 \</view> 96 ) ( 97 \<view style={styles form}> 98 \<text>{'inform the received token to proceed'}\</text> 99 \<textinput 100 style={styles form input} 101 value={usertoken} 102 placeholder={'token (6 digits)'} 103 onchangetext={(text) => setusertoken(text)} 104 autocapitalize={'none'} 105 keyboardtype={'default'} 106 /> 107 \<touchableopacity onpress={() => verifyotp()}> 108 \<view style={styles button}> 109 \<text style={styles button label}>{'verify'}\</text> 110 \</view> 111 \</touchableopacity> 112 \<touchableopacity onpress={() => requestotp()}> 113 \<view style={styles button}> 114 \<text style={styles button label}>{'resend token'}\</text> 115 \</view> 116 \</touchableopacity> 117 \</view> 118 )} 119 \</view> 120 ); 121 };1 import react, {fc, reactelement, usestate} from 'react'; 2 import {alert, text, textinput, touchableopacity, view} from 'react native'; 3 import parse from 'parse/react native'; 4 import {usenavigation} from '@react navigation/native'; 5 import styles from ' /styles'; 6	 7 export const userotp fc<{}> = ({}) reactelement => { 8 const navigation = usenavigation(); 9	 10 const \[userdata, setuserdata] = usestate(''); 11 const \[usertoken, setusertoken] = usestate(''); 12 const \[tokenrequested, settokenrequested] = usestate(false); 13	 14 const requestotp = async function () promise\<boolean> { 15 // note that this values come from state variables that we've declared before 16 const userdatavalue string = userdata; 17 // check if value is an email if it contains @ note that in a real 18 // app you need a much better validator for this field 19 const verificationtype string = 20 userdatavalue includes('@') === true ? 'email' 'sms'; 21 // we need to call it using await 22 try { 23 await parse cloud run('requestotp', { 24 userdata userdatavalue, 25 verificationtype verificationtype, 26 }); 27 // show token input field 28 settokenrequested(true); 29 alert alert('success!', `token requested via ${verificationtype}!`); 30 return true; 31 } catch (error) { 32 alert alert('error!', error message); 33 return false; 34 } 35 }; 36	 37 const verifyotp = async function () promise\<boolean> { 38 // note that this values come from state variables that we've declared before 39 const userdatavalue string = userdata; 40 const usertokenvalue string = usertoken; 41 // check if value is an email if it contains @ note that in a real 42 // app you need a much better validator for this field 43 const verificationtype string = 44 userdatavalue includes('@') === true ? 'email' 'sms'; 45 // we need the installation id to allow cloud code to create 46 // a new session and login user without password; this is obtained 47 // using a static method from parse 48 const parseinstallationid string = await parse getinstallationid(); 49 // we need to call it using await 50 try { 51 // verify otp, if successful, returns a sessionid 52 let response object = await parse cloud run('verifyotp', { 53 userdata userdatavalue, 54 verificationtype verificationtype, 55 usertoken usertokenvalue, 56 parseinstallationid parseinstallationid, 57 }); 58 if (response sessionid !== undefined) { 59 // use generated sessionid to become a user, 60 // logging in without needing to inform password and username 61 await parse user become(response sessionid); 62 const loggedinuser parse user = await parse user currentasync(); 63 alert alert( 64 'success!', 65 `user ${loggedinuser get('username')} has successfully signed in!`, 66 ); 67 // navigation navigate takes the user to the home screen 68 navigation navigate('home'); 69 return true; 70 } else { 71 throw response; 72 } 73 } catch (error) { 74 alert alert('error!', error message); 75 return false; 76 } 77 }; 78	 79 return ( 80 \<view style={styles login wrapper}> 81 {tokenrequested === false ? ( 82 \<view style={styles form}> 83 \<textinput 84 style={styles form input} 85 value={userdata} 86 placeholder={'email or mobile phone number'} 87 onchangetext={(text) => setuserdata(text)} 88 autocapitalize={'none'} 89 keyboardtype={'email address'} 90 /> 91 \<touchableopacity onpress={() => requestotp()}> 92 \<view style={styles button}> 93 \<text style={styles button label}>{'request otp'}\</text> 94 \</view> 95 \</touchableopacity> 96 \</view> 97 ) ( 98 \<view style={styles form}> 99 \<text>{'inform the received token to proceed'}\</text> 100 \<textinput 101 style={styles form input} 102 value={usertoken} 103 placeholder={'token (6 digits)'} 104 onchangetext={(text) => setusertoken(text)} 105 autocapitalize={'none'} 106 keyboardtype={'default'} 107 /> 108 \<touchableopacity onpress={() => verifyotp()}> 109 \<view style={styles button}> 110 \<text style={styles button label}>{'verify'}\</text> 111 \</view> 112 \</touchableopacity> 113 \<touchableopacity onpress={() => requestotp()}> 114 \<view style={styles button}> 115 \<text style={styles button label}>{'resend token'}\</text> 116 \</view> 117 \</touchableopacity> 118 \</view> 119 )} 120 \</view> 121 ); 122 }; 「 requestotp requestotp 」と「 verifyotp verifyotp 」の機能を詳しく見てみましょう。これらはそれぞれのcloud code関数を呼び出し、その応答を検証する役割を担っています。どのように機能するかの詳細は、コードのコメントで確認できます。 新しい画面を作成した後、あなたの app js app js (または app tsx app tsx ) にインポートして宣言します。その後、あなたの userlogin js userlogin js (または userlogin tsx userlogin tsx ) ファイルに新しいボタンを追加し、ユーザーがotp画面に移動できるようにします。 4 新しいotp機能のテスト アプリの変更をテストしてみましょう。まず、有効なメールアドレスと電話番号を含む新しいユーザーを登録します。電話番号には国際表記(e 164)形式を使用してください(例:+14155552671)。 次に、ログイン画面からotp画面に移動し、前と同じメールアドレスまたは電話番号を入力します。リクエストボタンをクリックすると、画面上のアクティブな入力が変更されるようなメッセージが表示されるはずです。 メールアドレスを入力した場合、otpトークンを含むメールが届くはずです。電話番号を入力した場合は、携帯電話にsmsテキストメッセージが届きます。メールには、sendgridテンプレートの設定に応じたメッセージが含まれているはずです。 otpトークンを入力し、確認をクリックします。すべてがうまくいけば、次のメッセージが表示されたホーム画面にいるはずです 結論 このガイドの最後に、react nativeアプリケーションにサードパーティサービスを統合するためにparse cloud code関数を使用する方法を学びました。次のガイドでは、parseでユーザーと作業する方法を学びます。