🛡️

Cognitoのログイン情報が書き換わったタイミングを検知するhooks作った

2024/08/05に公開

概要

今回はReactで動作しているSPAで、Cognitoを使っている場合のTipsです。
AWSのコンソールなんかもそうですが、同一ブラウザで複数タブで異なるアカウントで操作しようとすると、元々動かしていた方のアカウントの操作ができなくなり、リロードや再ログインを促されたりします(もしかしたら名称があるかも)。
あの仕組みをそのまま自作したので、たたき台として共有します。

バージョン情報

Reactのバージョンはちょい古めです。
関係してそうなものだけpackage.jsonから抜粋。

  • "@aws-amplify/ui-react": "^5.0.4"
  • "aws-amplify": "^5.3.4"
  • "react": "^18.2.0"

簡単に解説

aws-amplify/authを使っている場合、特にカスタムしていなければ認証情報はCookieに入っています。
その時のキーは以下のような形式です。

CognitoIdentityServiceProvider.{CLIENT_ID}.{SUB}.XXXX

XXXXの箇所はidTokenrefreshTokenなどが入りますが、それより前の部分は共通のルールです。
複数タブで操作している場合に、片方のタブで別アカウントログイン・もしくはログアウトをした場合、このCookieの値が変わります(もしくはクリアされます)。
なので、Cookieの中身を監視し、変更が検知できたタイミングで何らかの表示をしてあげればいいということになります。

完成したもの

以下が完成したhooksです。
subintervalを指定して、先ほどのCookieの値を監視する処理を行っています。
※パフォーマンスに影響しそうなので、厳密な仕様でないならintervalを伸ばしてもいいかもしれません。

import { useEffect, useRef, useState } from "react";
import Cookies from "js-cookie";

const useCognitoCookieChange = (sub: string, interval = 1000) => {
  const clientId = import.meta.env.VITE_APP_AUTH_USER_POOL_WEB_CLIENT_ID;
  // CognitoをIdProviderとした場合のidTokenが入っているCookie名
  const cookieName = `CognitoIdentityServiceProvider.${clientId}.${sub}.idToken`;
  const initialCookieValue = useRef(Cookies.get(cookieName));
  const [changed, setChanged] = useState<boolean>(false);

  useEffect(() => {
    const checkCookieChange = () => {
      const currentCookieValue = Cookies.get(cookieName);
      // 変更検知時
      if (currentCookieValue !== initialCookieValue.current) {
        // initialの値がなかった(=未ログインからログインした)場合は無視する
        if (initialCookieValue.current && !currentCookieValue) setChanged(true);
        initialCookieValue.current = currentCookieValue;
      }
    };

    const intervalId = setInterval(checkCookieChange, interval);

    return () => clearInterval(intervalId);
  }, [cookieName, setChanged, interval]);

  return {
    changed,
  };
};

export default useCognitoCookieChange;

最終的にこのhooksが返す値(changed)がtrueになったら「他のタブでログアウトが行われた」もしくは「他のタブで別ユーザでログインが行われた」ということになるので、モーダルやアラートを出してアクションを促します。
自分は「再読み込み」のようなボタンを表示させて、そのタブはリロードさせる運用としています。

おわりに

今回は別タブでのログインやログアウトを検知し、複数アカウントでのログインを防止するhooksについて紹介しました。
割と簡易に作っているので、いくつか穴抜けとなっているケースはあるかと思います。
その場合はマサカリ大歓迎です。

この記事の内容が役立ちましたら幸いです。

Discussion