Auth0を利用してReact Applicationに認証機能を追加する
SinglePageApplication(以下 SPA)では認証が必要な API にアクセスする際、認証情報を持ったアクセストークンを Authoriztion header に付けてアクセスします。アクセストークンが不正であればデータは返却されず、アクセストークンに問題がなければデータが返却されます。アクセストークンはローカルストレージやセッションストレージ、あるいは cookie を利用して保管して利用します。
フロントエンドでもアクセストークンのセットやリフレッシュなど実装に工程はいくつかありますが、サーバー側のアクセストークンの認証の仕組みはそれと比較にならないほど難解です。一般的なメールアドレスとパスワードというログイン方法以外に、Google や Apple、Twitter といったアカウントでログインできるソーシャルログイン、複数サービスにまたがってログイン可能な SSO などが求められるとよりコストの高い作業になります。
そんなわけで認証を作るのが主目的ではないなら、その辺りをまるっと提供してくれる外部サービスを使用するのが便利です。この記事では開発では無料で使いやすい Auth0 を使用します。
My Todo Note
この記事に関連した記事は下記のシリーズでまとめています。完全に独立した記事ではないため、この記事だけではコード上、不明な点があるかもしれません。
関連シリーズ:自分用の Dashboard サービスを作成する
開発用リポジトリ
My-Todo-Note は次のリポジトリで管理しています。
Auth0 の使用
Auth0 のアカウントを作成してログイン後、Applications から Application を作成します。Quick Start から React を選択し、手順に沿って進めます。開発環境では auth0 のライブラリを導入します。
yarn add @auth0/auth0-react
認証機能はアプリケーション全体に反映したいので index.tsx に Auth0Provider を設定します。
ReactDOM.render(
<Auth0Provider
domain={process.env.AUTH0_DOMAIN}
clientId={process.env.AUTH0_CLIENT_ID}
redirectUri={window.location.origin}
>
<App />
</Auth0Provider>,
document.getElementById("root")
);
// .env
AUTH0_DOMAIN="xxx.auth0.com"
AUTH0_CLIENT_ID="xxx...文字列"
domain、clientId のキーは.env から読み取るようにします。domain,clientId は Auth0 の Application Settings の Basic Information から確認ができます。
認証画面への遷移
チュートリアルに沿ってログイン画面への遷移ボタンを作成します。このボタンを押すと、Auth0 の認証画面に遷移し、ログインが成功すると Auth0Provider の redirectUri にリダイレクトされます。
const LoginButton = () => {
const { loginWithRedirect } = useAuth0();
return <button onClick={() => loginWithRedirect()}>Log In</button>;
};
Auth0 で許可する URL を設定する
ログイン機能を許可する URL、ログイン後に戻ることを許可する URL などを設定しておく必要があります。Auth0 で作成した Application から、Application URIs の項目に移動して設定します。まずは開発時の URL を設定しておきます。
localhost:8080 で開発しているなら、許可する URL に http://localhost:8080 を設定します。また、localhost ではなく IP で動作させているなら IP も同時に設定が必要です。http://192.xxx.xxx.x:8080 のような形です。
ログインの確認
設定が完了して LoginButton を押すと Auth0 のログイン画面に遷移できるようになります。
LoginButton から認証画面に遷移できたら適当に新規ユーザーを作成してログインします。作成したユーザー情報は Auth0 から確認や削除ができます。
ルーティングの改良
Auth0 でログイン後、開発画面にリダイレクトされたら認証用の情報を取得できる状態です。ユーザーの認証情報は useAuth0 hook から取得できます。この情報を使用してログイン中のみアクセスできるルートを保護します。
ログイン済みかどうかの確認は非同期処理で行われます。そのため、認証済みルートにアクセスする際はそのチェックが終了しているかも確認する必要があります。それらの情報は isLoading、isAuthenticated という変数で管理されています。
その辺りを組み込んで Route 設定を下記のようにします。
const UnAuthenticatedRoutes: RouteProps[] = [
{
path: "/",
component: Home,
exact: true,
},
];
const AuthenticatedRoutes: RouteProps[] = [
{
path: "/dashboard",
component: Dashboard,
},
];
const AppRoutes = () => {
const { isLoading } = useAuth0();
if (isLoading) {
return <div>Load中...</div>;
}
return (
<Switch>
{AuthenticatedRoutes.map((route, index) => (
<AuthenticatedRoute key={index} {...route} />
))}
{UnauthenticatedRoutes.map((route, index) => (
<Route key={index} {...route} />
))}
</Switch>
);
};
const AuthenticatedRoute: React.FC<RouteProps> = ({ component, ...rest }) => {
const { isAuthenticated } = useAuth0();
const renderRedicrect = () => {
return <Redirect to="/" />;
};
return (
<Route
{...rest}
component={isAuthenticated ? component : renderRedicrect}
/>
);
};
Auth0 からログイン済みかのチェックが終わっていない間はルートを render せず、終わってから表示します。また、認証が必要なルートは isAuthenticated で判定し、これが false の時はログインを求める画面にリダイレクトさせます。
アクセストークンの取得
認証が成功するとアクセストークンを取得できます。認証用の API にアクセスするときは、アクセストークンを Authorization header に付与して使用します。
const { getAccessTokenSilently } = useAuth0();
const token = await getAccessTokenSilently();
これで認証の組み込みの基本的なところが終わったので次の記事で、認証用 API にアクセスする仕組みを作成します。