認証技術はアプリや Web サービスに対して、よく実装されます。ID/パスワード認証などを実装した経験は誰しもがあるのではないでしょうか。簡単に実装しようと思えばできるものの、セキュリティへの懸念もあって年々複雑化しているのが認証でもあります。

例えばソーシャルサービスを使った OAuth2 認証や、PassKeys を使った認証などもあります。認証方法を忘れてしまった場合のリマインダーなども必要で、意外と使い勝手の良い実装は難しいものです。

そこで使ってみたいのが認証プロバイダーとして知られる Auth0 です。今回は PWA アプリに対して Auth0 を実装する流れを解説します。

Auth0 とは

Auth0 は、IDaaS と呼ばれるサービスの一つです。IDaaS は ID as a Service の略で、認証認可に関わる部分を SaaS で提供しています。単純な ID パスワード認証はもちろん、ソーシャル認証やハードウェアキーを用いた認証など、さまざまな手法に対応しています。

Auth0 は月間2.5万アカウントまでは無料で利用できます。最初は無料で使いつつ、規模が大きくなってきたら課金するといった流れで使い続けられます。

Auth0 のベースセットアップ

今回は Vite + React の組み合わせで、Auth0 を利用する流れを解説します。

Auth0 の設定

Auth0 でユーザー登録します。

ユーザー登録すると、デフォルトでアプリが登録されていますので、今回はこれを利用します。以下の設定を変更します。 http://localhost:5173 は Vite アプリのデフォルトの URL です。

  • アプリケーション種類
    シングルページアプリケーション
  • アプリケーションのログイン URL
    http://localhost:5173
  • 許可する Callback URL
    http://localhost:5173
  • 許可するログアウト URL
    http://localhost:5173

逆に、Auth0 からは以下の情報をメモしておきます。

  • ドメイン
  • クライアント ID

ベースになるアプリを生成する

まず、ベースになるアプリを生成します。React と TypeScript を有効にしています。 my-pwa-app は自分の作成するアプリの名前で、この名前でフォルダが生成されます。

npm create vite@latest my-pwa-app -- --template react-ts

依存するライブラリをインストールします。

cd my-pwa-app
npm install

Auth0 をインストールする

Auth0 のライブラリをインストールします。

npm install @auth0/auth0-react

PWA 関連のライブラリをインストールする

さらに PWA 関係のライブラリとしてキャッシュ操作を行う workbox をインストールします。 vite-plugin-pwa は Vite アプリで PWA を手軽に扱えるようにするプラグインです。

npm install -D workbox-core workbox-routing workbox-strategies workbox-precaching
npm install vite-plugin-pwa

アプリへの認証機能の実装

ベースができたので、認証を実装していきます。まず vite.config.ts を編集します。 VitePWA という vite-plugin-pwa を使って、PWA としてアプリケーションをインストールする際の設定を行います。

import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import { VitePWA } from "vite-plugin-pwa"

export default defineConfig({
  plugins: [
    react(),
    VitePWA({
      registerType: "autoUpdate",
      strategies: "injectManifest", // workbox-precaching を使う場合
      srcDir: "public",
      filename: "sw.js",
      includeAssets: ["favicon.svg", "robots.txt"],
      manifest: {
        name: "PWA Auth0 デモ",
        short_name: "Auth0PWA",
        start_url: "/",
        display: "standalone",
        background_color: "#ffffff",
        theme_color: "#317EFB",
        icons: [
          {
            src: "icon-192.png",
            sizes: "192x192",
            type: "image/png"
          },
          {
            src: "icon-512.png",
            sizes: "512x512",
            type: "image/png"
          }
        ]
      }
    })
  ]
});

次に public/sw.js を作成して、 Workbox を読み込みます。今回はこれ以上特に Workbox は使っていません。キャッシュを使う場合には、このファイルの中に記述していきます。

import { precacheAndRoute } from "workbox-precaching";

precacheAndRoute(self.__WB_MANIFEST || []);

src/main.ts の編集

Service Worker 登録は VitePWA が自動で行うため、手動での navigator.serviceWorker.register() は不要になりました。以下のコードは削除してください。

- if ("serviceWorker" in navigator) {
-   window.addEventListener("load", () => {
-     navigator.serviceWorker.register("/sw.js");
-   });
- }

次に Auth0 を読み込みます。 domainclientId は Auth0 の管理画面で取得したものを設定してください。環境変数にしておくと安全です。

import { Auth0Provider } from "@auth0/auth0-react";

// .env.local などに VITE_AUTH0_DOMAIN, VITE_AUTH0_CLIENT_ID を定義
const domain = import.meta.env.VITE_AUTH0_DOMAIN;
const clientId = import.meta.env.VITE_AUTH0_CLIENT_ID;

そして ReactDOM.createRoot の内容を以下のように書き換えます。

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <Auth0Provider
      domain={domain}
      clientId={clientId}
      authorizationParams={{
        redirect_uri: window.location.origin
      }}
      useRefreshTokens={true} // トークンの自動更新に重要
      cacheLocation="localstorage" // PWA 対応のため localStorage 推奨
    >
      <App />
    </Auth0Provider>
  </React.StrictMode>,
);

src/App.tsx の編集

src/App.tsx は最初に表示される画面です。この内容を以下のように書き換えます。 useAuth0<Auth0Provider /> で囲んであるので利用できる機能になります。

useAuth0 実行後に返ってくるオブジェクトは以下のような意味になります。

  • loginWithRedirect
    ログイン処理を実行するメソッド(Auth0 にリダイレクトします)
  • logout
    ログアウト処理を実行するメソッド
  • user
    認証した後のユーザー情報が入っているオブジェクト
  • isAuthenticated
    認証判定用のオブジェクト
  • isLoading
    Auth0 初期化前は true 、初期化が終わると false になります
import { useAuth0 } from "@auth0/auth0-react";

function App() {
  const { loginWithRedirect, logout, user, isAuthenticated, isLoading } = useAuth0();

  if (isLoading) return <p>Loading...</p>;

  return (
    <div>
      <h1>PWA Auth0 Sample</h1>
      {isAuthenticated ? (
        <>
          <p>こんにちは、{user?.name} さん</p>
          <button onClick={() => logout({ logoutParams: { returnTo: window.location.origin } })}>
            ログアウト
          </button>
        </>
      ) : (
        <button onClick={() => loginWithRedirect()}>ログイン</button>
      )}
    </div>
  );
}

export default App;

試してみる

では実装が終わったので、開発用サーバーで試してみます。

npm run dev

最初はログインボタンが表示されています。

ログインは Auth0 上で行います。

認証が終わって、最初の画面に戻ってきました。ユーザー名が表示され、ログアウトボタンも出ています。

これで認証の流れを一通り体験できます。

他の認証技術との比較

アプリへの認証実装として、他にもいくつかの選択肢があります。

  • 独自の実装
  • Firebase Auth や Supabase などの BaaS 利用
  • Apple などのソーシャル認証

独自の実装は、カスタマイズ性の高さがメリットでしょう。しかし、最初に述べた通り、認証周りの技術は複雑化しており、多彩な認証に対応するのは工数がかかります。また、認証情報を保存することにより、万一漏洩した場合のリスクも存在します。

Firebase Auth や Supabase をはじめとした BaaS を利用する方法もあります。メリットは Auth0 のように認証を外出しできること、そしてデータベースやファイルストアなどの別な技術と組み合わせられる点が挙げられます。デメリットとしては、Auth0 のような専門サービスではないため、対応していない認証技術もあることでしょう。また、複数のアプリや Web サービスで認証を共有できない、またはカスタマイズが必要になります。

最後にソーシャル認証だけを使うのであれば、Apple や Facebook などが提供するものを利用する方法もあります。特に iOS アプリではソーシャル認証を利用する場合には、Apple のガイドライン上、他社 SSO を使って 新規アカウント作成 ができる場合に Sign in with Apple の実装が必須 となっています。ソーシャル認証は SDK が提供されている場合も多く、実装を手軽に行えるのがメリットです。デメリットとしては、開発版・ステージング版・本番環境のそれぞれでアプリを分ける必要があるケースが多いことです。開発やテストで若干の手間がかかります。

どれも一長一短がありますので、どれを選択するかはアプリやサービスの要件によって異なります。アプリのみに対応しているものもあれば、Web やデスクトップでの認証共通化できるものもあります。そうした利用ケースなども考えた上で選択してください。

まとめ

今回は Auth0 と React を使って、認証を実装しました。PWA アプリとして実装しているので、アプリのようにインストールして利用できます。今回のデモをベースに、ぜひ Auth0 を使ったアプリ開発にチャレンジしてください。