blog

React Native Expo Rushチュートリアル6 - 認証プロセス

ほとんどのアプリケーションでは、ユーザーに関連するデータやその他のプライベートなコンテンツにアクセスするために、何らかの方法でユーザーを認証する必要があります。通常、フローは次のようになります。 ユー...

Jul 15, 2020 · 7 min. read
シェア

認証プロセス

ほとんどのアプリケーションは、ユーザに関連するデータやその他のプライベートなコンテンツにアクセスす るために、何らかの方法でユーザを認証する必要があります。通常、このプロセスは以下のようなものです。

  • ユーザーはアプリケーションを開きます。
  • アプリケーションは、永続ストレージから認証状態をロードします。
  • 状態がロードされると、有効な認証状態がロードされているかどうかに応じて、ユーザには 認証画面またはメイン・アプリケーションが表示されます。
  • ユーザーがサインアウトすると、認証ステータスがクリアされ、ログイン画面に戻ります。

注:「ログイン画面」とあるのは、通常複数の画面があるからです。ユーザー名とパスワードの入力欄があるメイン画面、「パスワードをお忘れの方」用の画面、そして登録用の画面があります。

必要なもの

これは、ログインプロセスから望まれる動作です:ユーザーがログインしたときに、ログインプロセスを削除し、すべてのログイン関連画面を解除し、ハードウェアの戻るボタンが押されたときに、ログインプロセスに戻ることができないようにしたいのです。

仕組み

様々な条件に基づいて、異なる画面を定義することができます。例えば、ユーザがログインしている場合、ホームページ、プロフィール、設定などを定義することができます。ユーザがログインしていない場合は、ログイン画面とログイン画面を定義できます:

isSignedIn ? ( <> <Stack.Screen name="Home" component={HomeScreen} /> <Stack.Screen name="Profile" component={ProfileScreen} /> <Stack.Screen name="Settings" component={SettingsScreen} /> </> ) : ( <> <Stack.Screen name="SignIn" component={SignInScreen} /> <Stack.Screen name="SignUp" component={SignUpScreen} /> </> )

これらの画面を定義した後、isSignedInがtrueの場合、React NavigationはHome、Profile、Settings画面のみを表示し、isSignedInがfalseの場合、React NavigationはSignInとSignUp画面を表示します。これにより、ユーザーがログインしていない場合は、Home、Profile、Settings画面に移動することができなくなり、ユーザーがログインしている場合は、SignInとSignUp画面に移動することができなくなります。

このモデルは他のルーティングライブラリで長い間使われており、一般的に "保護されたルーティング "として知られています。ここでは、ユーザがログインする必要のある画面は「保護」され、ユーザがログインしていない場合、他の手段でナビゲートすることはできません。

マジックは、isSignedIn変数の値が変化したときに起こります。最初はisSignedInがfalseだとします。これはSignIn画面とSignUp画面のどちらかを表示できることを意味します。ユーザーがログインした後、isSignedInの値はtrueに変更され、react Navigationは定義されたSignInとSignUp画面を表示しません。なぜなら、isSignedInがtrueの場合、それが最初に定義された画面だからです。

このような設定を使用する場合、navigation.navigate('Home')を呼び出して手動でホーム画面に移動する必要はないことに注意してください。isSignedInがtrueになると、React Navigationは自動的にホーム画面に移動します。

これは、React Navigationの新機能である、プロパティや状態に基づいてナビゲータの画面定義を動的に定義・変更する機能を利用したものです。この例ではスタックナビゲータを示していますが、どのナビゲータにも同じアプローチを使用できます。

変数に基づいて異なる画面を条件付きで定義することで、正しい画面が表示されるように追加のロジックを必要としないシンプルな方法で認証フローを実装することができます。

定義された画面

ナビゲータでは、対応する画面を条件付きで定義することが可能です。例えば、3つの画面があるとします。

SplashScreen - トークンが復元されると、スプラッシュ画面またはローディング画面が表示されます。SignInScreen - ユーザがまだログインしていない場合に表示されます。HomeScreen - この画面は、ユーザがログインしている場合に表示されます。

ナビゲーターはこのようになります:

if (state.isLoading) {
 // We haven't finished checking for the token yet
 return <SplashScreen />;
}
return (
 <Stack.Navigator>
 {state.userToken == null ? (
 // No token found, user isn't signed in
 <Stack.Screen
 name="SignIn"
 component={SignInScreen}
 options={{
 title: 'Sign in',
 // When logging out, a pop animation feels intuitive
 // You can remove this if you want the default 'push' animation
 animationTypeForReplace: state.isSignout ? 'pop' : 'push',
 }}
 />
 ) : (
 // User is signed in
 <Stack.Screen name="Home" component={HomeScreen} />
 )}
 </Stack.Navigator>
);

上記のスニペットでは、isLoadingはまだトークンがあるかどうかをチェックしていることを意味し、これは通常、AsyncStorageにトークンがあるかどうかをチェックし、トークンを検証することで行うことができます。これは通常、AsyncStorageにトークンがあるかどうかを確認し、トークンを検証することで行うことができます。トークンを取得した後、それが有効であればuserTokenを設定する必要があります。また、isSignoutと呼ばれるステートがあり、サインアウト時にさまざまなアニメーションを使用できます。

スクリーンが条件付きで定義されているのは、これらの状態変数に基づいていることに注意することが重要です。

  • サインイン画面は、userTokenがNULLの場合のみ定義されます。
  • ホーム画面はuserTokenがnullでない場合にのみ定義されます。

ここでは、1つのケースにつき1つの画面を定義することを条件としていますが、複数の画面を定義することもできます。例えば、ユーザがログインしていない場合、パスワードのリセットやログインなどの画面も定義したい場合があります。同様に、ログイン後にアクセスできる画面についても、複数の画面を定義することができます。複数の画面を定義するには、React.Fragment.Reactを使います。

state.userToken == null ? ( <> <Stack.Screen name="SignIn" component={SignInScreen} /> <Stack.Screen name="SignUp" component={SignUpScreen} /> <Stack.Screen name="ResetPassword" component={ResetPassword} /> </> ) : ( <> <Stack.Screen name="Home" component={HomeScreen} /> <Stack.Screen name="Profile" component={ProfileScreen} /> </> );

回復トークンを実装するためのロジック

注意: 以下は、アプリケーションに認証ロジックを実装する方法の一例です。そのまま従う必要はありません。

先ほどのスニペットから、3つの状態変数が必要であることがわかります。

  • isLoading - トークンが AsyncStorage に保存されたかどうかを確認する際に true を設定します。
  • isSignout - ユーザがサインアウトした場合に true を設定し、そうでない場合は false を設定します。
  • userToken - ユーザーのトークン。これがnullでない場合、そのユーザーはログインしていると見なされます。そのため、必須です。

トークン、ログイン、ログアウトを復元するロジックを追加します。ログインとログアウトのメソッドを他のコンポーネントに公開します。

このガイドではReact.useReducerとReact.useContextを使用しますが、ReduxやMobxなどの状態管理ライブラリを使用している場合は、この機能の代わりにそれらを使用することができます。実際、大規模なアプリケーションでは、グローバルな状態管理ライブラリのほうが認証トークンの保存に適しています。同じアプローチを状態管理ライブラリにも適用できます。

まず、認証に必要なメソッドを公開するためのコンテキストを作成する必要があります。

import * as React from 'react'; const AuthContext = React.createContext();

コンポーネントは次のようになります:

import * as React from 'react'; import AsyncStorage from '@react-native-community/async-storage'; export default function App({ navigation }) { const [state, dispatch] = React.useReducer( (prevState, action) => { switch (action.type) { case 'RESTORE_TOKEN': return { ...prevState, userToken: action.token, isLoading: false, }; case 'SIGN_IN': return { ...prevState, isSignout: false, userToken: action.token, }; case 'SIGN_OUT': return { ...prevState, isSignout: true, userToken: null, }; } }, { isLoading: true, isSignout: false, userToken: null, } ); React.useEffect(() => { // Fetch the token from storage then navigate to our appropriate place const bootstrapAsync = async () => { let userToken; try { userToken = await AsyncStorage.getItem('userToken'); } catch (e) { // Restoring token failed } // After restoring token, we may need to validate it in production apps // This will switch to the App screen or Auth screen and this loading // screen will be unmounted and thrown away. dispatch({ type: 'RESTORE_TOKEN', token: userToken }); }; bootstrapAsync(); }, []); const authContext = React.useMemo( () => ({ signIn: async data => { // In a production app, we need to send some data (usually username, password) to server and get a token // We will also need to handle errors if sign in failed // After getting token, we need to persist the token using `AsyncStorage` // In the example, we'll use a dummy token dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' }); }, signOut: () => dispatch({ type: 'SIGN_OUT' }), signUp: async data => { // In a production app, we need to send user data to server and get a token // We will also need to handle errors if sign up failed // After getting token, we need to persist the token using `AsyncStorage` // In the example, we'll use a dummy token dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' }); }, }), [] ); return ( <AuthContext.Provider value={authContext}> <Stack.Navigator> {state.userToken == null ? ( <Stack.Screen name="SignIn" component={SignInScreen} /> ) : ( <Stack.Screen name="Home" component={HomeScreen} /> )} </Stack.Navigator> </AuthContext.Provider> ); }

充填部品

ナビゲーションの一部ではない、認証画面用のテキスト入力とボタンの実装方法について話しているわけではありません。プレースホルダーの内容を埋めるだけです。

function SignInScreen() {
 const [username, setUsername] = React.useState('');
 const [password, setPassword] = React.useState('');
 const { signIn } = React.useContext(AuthContext);
 return (
 <View>
 <TextInput
 placeholder="Username"
 value={username}
 onChangeText={setUsername}
 />
 <TextInput
 placeholder="Password"
 value={password}
 onChangeText={setPassword}
 secureTextEntry
 />
 <Button title="Sign in" onPress={() => signIn({ username, password })} />
 </View>
 );
}
Read next

CSSのフロントエンド学習 - CSSの3つの特徴

同じセレクタは同じスタイルを設定し、一方のスタイルは競合するスタイルを上書きします。CSSの継承:子タグは、テキストの色やフォントサイズなど、特定のスタイルを親から継承します。継承を適切に使用することで、コードを簡素化し、CSS スタイルの複雑さを軽減できます。 同じ要素に複数のセレクタが指定されている場合、優先順位が作成されます。 ...

Jul 15, 2020 · 2 min read