Auth0のProtected Routeをreact-router-dom v6に対応させる

24009 ワード

Auth0 React SDKを使っていたアプリが、react-router-domをv5からv6にアップデートしたら動作しなくなりました。
Auth0のブログや、サンプルコードを見て直すことになったので、修正のポイントを書いていきます。

この記事の前提

  • ルーティングにreact-router-domを使用している
  • ユーザ認証にAuth0 React SDKを使っている

react-router-dom v5の書き方

Auth0のサンプルコードにあるやり方ですが、カスタムのRouteコンポーネントを作って、認証が必要なパスを保護していました。
react-router-dom v5では、次のような書き方になります。

// ProtectedRoute.js
import { withAuthenticationRequired } from "@auth0/auth0-react";
import { Route } from "react-router-dom"; // v5

const ProtectedRoute = ({ component, ...options }) => {
  return <Route component={withAuthenticationRequired(component, {})} {...options} />;
};
export default ProtectedRoute;

カスタムのRouteコンポーネント(ProtectedRoute)を使ったルーティングはこのようになります。

// App.js
import ProtectedRoute from './ProtectedRoute';
import { BrowserRouter, Switch, Route } from 'react-router-dom'; // v5

function App() {
  return (
    <BrowserRouter>
      <Switch>
        {/* 認証が必要なパス */}
        <ProtectedRoute exact path='/' component={Home} />
        {/* 認証がいらないパス */}
	<Route path='/logout' component={Logout} />
      </Switch>
    </BrowserRouter>
  );
}
export default App;

react-router-dom v6の書き方

react-router-domをv6にアップデートしてからは、記法が変わったため、以下のようにコード修正が必要になりました。

// ProtectedRoute.js
import { withAuthenticationRequired } from '@auth0/auth0-react';

function ProtectedRoute({ component }) {
  const Component = withAuthenticationRequired(component, {});
  return <Component />; // Routeではなく、Componentにする
}
export default ProtectedRoute;

v6になってからは、レンダリングする要素をelementで指定するようになったため、上のコンポーネントを使用して次のように書きます。

// App.js
import ProtectedRoute from './components/ProtectedRoute';
import { BrowserRouter, Routes, Route } from 'react-router-dom'; // v6

function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* 認証が必要なパス */}
        <Route index element={<ProtectedRoute component={Home} />} />
        {/* 認証がいらないパス */}
        <Route path="logout" element={<Logout />} />
      </Routes>
    </BrowserRouter>
  );
}
export default App;

Auth0ProviderのonRedirectCallbackの実装

Auth0 React SDKのサンプルコードでは、認証後、ユーザのアクセスしたパスに遷移するようにコールバックが設定されています。
react-router-dom v6では、BrowserRouterの中にAuth0Providerを入れることで、同様のコールバックを実装することができます。

// Auth0Provider.js
import { Auth0Provider as Provider } from '@auth0/auth0-react';
import { useNavigate } from 'react-router-dom'; // v6

function Auth0Provider({ children }) { // 任意:カスタムのAuth0Provider
  const navigate = useNavigate();
  const domain = process.env.REACT_APP_AUTH0_DOMAIN;
  const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID;

  const onRedirectCallback = (appState) => {
    // navigationはBrowserRouterの中なら使用可能
    navigate(appState?.returnTo || window.location.pathname);
  };
  ...
  return (
    <Provider domain={domain} clientId={clientId} redirectUri={window.location.origin} onRedirectCallback={onRedirectCallback}>
      {children}
    </Provider>
  );
}
export default Auth0Provider;
// index.js
import Auth0Provider from './hooks/Auth0Provider'; // カスタムのAuth0Provider
import { BrowserRouter } from 'react-router-dom';
import App from './App';
...
root.render(
  <React.StrictMode>
    <BrowserRouter>
      {/* BrowserRouterの中に入れる */}
      <Auth0Provider>
        <App />
      </Auth0Provider>
    </BrowserRouter>
  </React.StrictMode>
);

今回の記事は、Auth0 Developer Hubのサンプルコードを参考にしています。