Auth0の認証トークンの保存方法がよくわかっていなかった

個人開発をする時にログイン周りをいつもFirebase Authenticationを使用していたため、他のIDaaSを使ってみたくなり、今回はAuth0を使ってみることにしました。

Auth0を使用していくうちに疑問が浮かんだのでメモのためにまとめておきます。

疑問

  • 許可されたAPIを叩く際にAuthorization headerで付与するトークンはどこに保持しておくのか
    • 一度ログインしたら一定期間はクライアント側に保存しておく?
    • 一回一回、認可サーバーに問い合わせてアクセストークンを取得してからサーバーにいく?

この辺りがよくわからなくなってしまったので調べてみることにしました。

環境

  • webを想定
  • auth0-reactというパッケージを使用
    • これはauth0-spa-jsをhooksのような実装にラップしたもので実際の実装はほとんどがこちらにある

github.com

また実装を追いやすいように専用のsampleアプリを作りました。 (https://github.com/haga0531/auth0-react-sample)

認証の流れ

Reactアプリ側でログインボタンを押す

auth0-reactloginWithRedirectを呼ぶようにしています。

<button onClick={() => loginWithRedirect()}>Log in</button>

loginWithRedirectの実装を見てます。

  /**
   * ```js
   * await loginWithRedirect(options);
   * ```
   *
   * Performs a redirect to `/authorize` using the parameters
   * provided as arguments. Random and secure `state` and `nonce`
   * parameters will be auto-generated.
   */
  loginWithRedirect: (options?: RedirectLoginOptions) => Promise<void>;

ここで行っていることとしては以下のようです。

  1. パラメータをセットして、 https://YOUR_DOMAIN/authorizeに移動
  2. エンドユーザーをhttps://YOUR_DOMAIN/loginの画面にリダイレクト

f:id:hagaahiro:20220110123444p:plain

またこの時のURLは以下のようになっています。 https://YOUR_DOMAIN/login?state=g6Fo2SBjNTRyanlVa3ZqeHN4d1htTnh&...

ドキュメントにも詳しく書かれていました。

ログインができ、isAuthenticatedがtrueになる

sampleアプリの実装では以下のようになっているため、yarn startを実行してlocalhostにアクセスすると、Log outボタンが表示されています。

import React from "react";
import "./App.css";
import { useAuth0 } from "@auth0/auth0-react";

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

  return (
    <div className="App">
      <header className="App-header">
        {!isAuthenticated ? (
          <button onClick={() => loginWithRedirect()}>Log in</button>
        ) : (
          <button onClick={() => logout()}>Log out</button>
        )}
      </header>
    </div>
  );
}

export default App;

疑問: 許可されたAPIを叩く際にAuthorization headerで付与するトークンはどこに保持しておくのか

ここで当初の疑問を思い出します。 許可されたAPIを叩く際にAuthorization headerで付与するトークンはどこに保持しておくのか

ドキュメントによるとAuth0のデフォルトの実装だとブラウザのインメモリに保存されるらしいです。

Use Auth0 SPA SDK whose default storage option is in-memory storage to leverage both Web Workers and JavaScript closures depending on the type of token.

なるほど、クライアントに保存しておくらしい。

しかし、この記述の下にこう書いてありました。

The in-memory method for browser storage does not provide persistence across page refreshes and browser tabs.

なるほど。インメモリだとリロードしたりブラウザのタブを変えたりしたら、ログイン状態が保持されないのか。

試してみます。 f:id:hagaahiro:20220110124038g:plain あれ、リロードしたもちょっとガチャってなるだけど、ログイン状態に戻った(isAuthenticatedがtrueに)。。

さらに疑問が生まれます。

Auth0のデフォルトの実装(tokenはブラウザのインメモリに保存される)のはずだけどリロードしたりブラウザ変えてもログイン状態を保持しているのはなぜだ、、

こういう時のnetworkタブということでこちらをを見てみることにしました。

見慣れないパラメータ、prompt=noneresponse_mode=web_messageを発見します。 f:id:hagaahiro:20220110124434p:plain

こちらを調べてみるとAuth0のドキュメントがヒットしました。

auth0.com

どうやらAuth0ではSilent Authenticationという仕組みでこのパラメータを使っているようです。 また以下のような記述がありました。

By default, auth0-spa-js uses the prompt=none and response_mode=web_message flow for silent auth, which depends on the user's Auth0 session. https://github.com/auth0/auth0-spa-js/blob/master/FAQ.md#using-multi-factor-authentication-mfa

response_mode=web_messageについても調べてみました。 こちらの記事がとてもわかりやすく書かれていました。

qiita.com

また例を出すが、Auth0 の SPA 用ライブラリ auth0/auth0-spa-js は Silent Authentication を使っている。初回表示時に prompt=none をつけた /authorize リクエストを裏で実行して、セッションが残っていればアプリケーションを自動的にログイン済み状態にしてくれる。

なるほど。In-memory方式で保存されているがリロードをしてもログイン状態を保持するという実装になっているのか。

なんとなく理解することができました。

Silent Authenticationにも欠点があることが述べられていますが、当初の疑問は解決された気がするのでここまでにします。

参照