タブと端末の同期:ログアウト、権限、カート
すべての場所から一度にログアウトし、取り消した権限をすぐに反映し、カートや設定をユーザーが開いているすべてのタブと端末でそろえます。
ユーザーはアプリを複数のタブや端末で開いたままにしていて、それぞれは再読み込みするまで変化を知りません。共用パソコンでログアウトしても、スマートフォンのタブにはまだそのアカウントが表示されています。管理者が同僚の編集権限を外しても、同僚は再読み込みするまで編集を続けられます。スマートフォンでカートに商品を入れても、ノートパソコンのタブではカートは空のままです。Hibira なら、サーバーが開いているアプリのすべてに一度で知らせます。
ユーザーが得るもの
- 「すべての端末からログアウト」で、開いているセッションが本当にすぐ閉じる。
- ロールの変更や権限の取り消しが再読み込みなしで反映される。
- どのタブ、どの端末でもカート、設定、未読数が同じ。
使う機能
- リアルタイムのアプリ内通知と同じユーザー認証(
pusher.signin())。サインインした各接続がどのユーザーのものか分かります。 sendToUser。1 回の呼び出しで、そのユーザーのすべてのタブと端末に届きます。terminateUserConnections。そのユーザーとしてサインインしているすべての接続を閉じます。
あるユーザーの状態が変わったとき
サーバーログアウト、新しいロール、カート
sendToUser()Hibira
WebSocket
すべてのタブと端末そのユーザーのもの
すべての端末からログアウト
サーバーセッションを取り消し済み
すべて切断
Hibira
閉じる
すべての接続そのユーザーとしてサインイン中
ステップ 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 で認可して使えます。 - カート全体ではなく件数と合計を送ります。イベントのサイズはプランによって異なります。料金をご覧ください。