← ユースケース

タブと端末の同期:ログアウト、権限、カート

すべての場所から一度にログアウトし、取り消した権限をすぐに反映し、カートや設定をユーザーが開いているすべてのタブと端末でそろえます。

ユーザーはアプリを複数のタブや端末で開いたままにしていて、それぞれは再読み込みするまで変化を知りません。共用パソコンでログアウトしても、スマートフォンのタブにはまだそのアカウントが表示されています。管理者が同僚の編集権限を外しても、同僚は再読み込みするまで編集を続けられます。スマートフォンでカートに商品を入れても、ノートパソコンのタブではカートは空のままです。Hibira なら、サーバーが開いているアプリのすべてに一度で知らせます。

ユーザーが得るもの

  • 「すべての端末からログアウト」で、開いているセッションが本当にすぐ閉じる。
  • ロールの変更や権限の取り消しが再読み込みなしで反映される。
  • どのタブ、どの端末でもカート、設定、未読数が同じ。

使う機能

  • リアルタイムのアプリ内通知と同じユーザー認証(pusher.signin())。サインインした各接続がどのユーザーのものか分かります。
  • sendToUser。1 回の呼び出しで、そのユーザーのすべてのタブと端末に届きます。
  • terminateUserConnections。そのユーザーとしてサインインしているすべての接続を閉じます。

あるユーザーの状態が変わったとき

サーバーログアウト、新しいロール、カート
sendToUser()
Hibira
WebSocket
すべてのタブと端末そのユーザーのもの

すべての端末からログアウト

サーバーセッションを取り消し済み
すべて切断
Hibira
閉じる
すべての接続そのユーザーとしてサインイン中
サーバーからの 1 回の呼び出しが、ユーザーが開いているアプリのすべてに届く

ステップ 1:すべての端末からログアウトする

まずデータベースでセッションを取り消し、開いているアプリすべてに知らせてから、接続を閉じます:

app.post('/account/sign-out-everywhere', requireLogin, async (req, res) => {
  const id = String(req.user.id);
  await db.sessions.revokeAll(id);
  await pusher.sendToUser(id, 'signed-out', {});
  await pusher.terminateUserConnections(id);
  res.sendStatus(204);
});

閉じられた接続は再びサインインできません。ユーザー認証エンドポイントが、取り消されたセッションをもう受け付けないためです。

ステップ 2:権限の変更をすぐ反映する

管理者が誰かのロールを変えたら、合図を送ります。ページはサーバーから権限を取り直し、判断の根拠は常にサーバーです:

await db.members.update(member.id, { role: 'viewer' });
await pusher.sendToUser(String(member.userId), 'permissions-changed', {});

ステップ 3:どこでも同じカートにする

カートが変わるたびに、ヘッダーに表示する概要を送ります:

app.post('/api/cart/items', requireLogin, async (req, res) => {
  const cart = await db.carts.add(req.user.id, req.body);
  await pusher.sendToUser(String(req.user.id), 'cart', { count: cart.count, total: cart.total });
  res.json(cart);
});

ステップ 4:すべてのタブで反応する

pusher.user.bind('signed-out', () => location.assign('/login'));
pusher.user.bind('permissions-changed', async () => {
  applyPermissions(await fetch('/api/me/permissions').then((r) => r.json()));
});
pusher.user.bind('cart', ({ count, total }) => cartBadge.set(count, total));

ヒント

  • セキュリティ上、イベントそのものは信用しません。「もう一度確認して」という意味だけです。API は引き続きリクエストごとにセッションとロールを確認します。
  • BroadcastChannel は同じブラウザーのタブ同士を同期しますが、ほかの端末には届かず、管理者やサーバーによる変更も受け取れません。
  • ゲストのカートは、カートごとのプライベートチャンネル(private-cart-<id>)をカートの Cookie で認可して使えます。
  • カート全体ではなく件数と合計を送ります。イベントのサイズはプランによって異なります。料金をご覧ください。