「権限管理設計」は“技術”じゃなくて“信頼”の設計
位置情報、カメラ、マイク、通知、センサー、ストレージ…。
Web API の多くは「権限(permission)」をユーザーに確認してから使います。
ここで大事なのは、
「どうやって権限を取るか」よりも
「ユーザーにどう信頼してもらうか」 という視点です。
権限設計が雑だと、
「なんか怖いサイト」「よく分からないけど全部聞いてくるサイト」になります。
逆に、丁寧に設計できると、それだけで“ちゃんとしてるサービス”に見えます。
原則 1: 「必要なときに、必要なものだけ」聞く
まとめて全部聞くのは最悪の UX
よくある悪いパターンは、
ページを開いた瞬間に、いきなり権限ダイアログが連発する設計です。
位置情報も
通知も
カメラも
マイクも
「とりあえず全部許可してね」みたいなやつです。
ユーザーからすると、
何に使うのか分からない
今それが必要なのか分からない
とりあえず「拒否」したくなる
という心理になります。
権限設計の基本は、
「その機能を使う直前に、その機能に必要な権限だけを聞く」 ことです。
位置情報が必要になるのは「近くの店を探すボタン」を押したとき
カメラが必要になるのは「写真を撮るボタン」を押したとき
通知が必要になるのは「通知をオンにする設定」をユーザーが選んだとき
このタイミングで初めて、
ブラウザの権限ダイアログを出すようにします。
例: 位置情報を「ボタンを押したときだけ」聞く
HTML:
<p>
現在地を使って、近くのカフェを表示できます。<br>
位置情報はサーバーに保存せず、このページ内だけで利用します。
</p>
<button id="nearCafeBtn">近くのカフェを探す</button>
<p id="message"></p>
JavaScript:
const btn = document.querySelector("#nearCafeBtn");
const message = document.querySelector("#message");
btn.addEventListener("click", () => {
if (!("geolocation" in navigator)) {
message.textContent = "このブラウザは位置情報に対応していません。";
return;
}
message.textContent = "ブラウザのダイアログで「許可」を選ぶと、近くのカフェを表示できます。";
navigator.geolocation.getCurrentPosition(
(pos) => {
message.textContent = "現在地を取得しました。カフェを検索中…";
// 検索処理を書く
},
(err) => {
if (err.code === err.PERMISSION_DENIED) {
message.textContent = "位置情報の利用が拒否されました。手動でエリアを選択できます。";
} else {
message.textContent = "位置情報を取得できませんでした。";
}
}
);
});
JavaScriptここでやっているのは、
ユーザーの操作(ボタン)をきっかけにする
その権限が何に使われるかを事前に説明する
拒否されたときのルートも用意する
という「権限設計の基本形」です。
原則 2: 「用途」と「範囲」を自分の言葉で説明する
ブラウザのダイアログ任せにしない
ブラウザは「位置情報を許可しますか?」と聞いてくれますが、
それだけだとユーザーはこう思います。
何に使うの?
どこまで使われるの?
保存されるの?
だから、アプリ側で先に説明します。
何のために使うのか(例: 近くのカフェを表示するため)
どこまで使うのか(例: このページ内だけで使い、サーバーには保存しない)
いつ使うのか(例: ボタンを押したときだけ)
この 3 つを短く書けると、かなり安心感が出ます。
例: 通知権限を「設定画面」で丁寧に聞く
HTML:
<p>
お気に入りのカフェのセール情報を通知で受け取ることができます。<br>
通知は 1 日 1 回までで、いつでもオフにできます。
</p>
<button id="enableNotifyBtn">通知をオンにする</button>
<p id="notifyMessage"></p>
JavaScript:
const btn = document.querySelector("#enableNotifyBtn");
const msg = document.querySelector("#notifyMessage");
btn.addEventListener("click", async () => {
if (!("Notification" in window)) {
msg.textContent = "このブラウザは通知に対応していません。";
return;
}
msg.textContent = "ブラウザのダイアログで「許可」を選ぶと、セール情報を通知できます。";
const result = await Notification.requestPermission();
if (result === "granted") {
msg.textContent = "通知が有効になりました。いつでも設定からオフにできます。";
// 通知登録処理を書く
} else if (result === "denied") {
msg.textContent = "通知は拒否されました。後からブラウザの設定で変更できます。";
} else {
msg.textContent = "通知の設定は保留されました。";
}
});
JavaScriptここで大事なのは、
「ユーザーの不安を先回りして潰す説明」 を入れていることです。
頻度(どれくらいの頻度で通知するか)
取り消し可能性(いつでもオフにできるか)
このあたりを書いておくと、
「一度許可したら終わりではない」と伝わって、心理的ハードルが下がります。
原則 3: 「拒否されても成立する」設計にする
権限は“取れたらラッキー”くらいの気持ちで設計する
権限は、拒否されるのが普通です。
「拒否されたら何もできません」は、設計として弱いです。
位置情報が拒否されたら、エリアを手動選択させる
カメラが拒否されたら、画像アップロードで代替する
通知が拒否されたら、メールやページ内のバッジで知らせる
こういう「代替ルート」を用意しておくと、
権限を取れなくてもサービスとして成立します。
例: 位置情報がなくても検索できる UI
HTML:
<p>
現在地を使って近くのカフェを探すか、エリアを選択してください。
</p>
<button id="useLocationBtn">現在地で探す</button>
<select id="areaSelect">
<option value="">エリアを選択</option>
<option value="shinjuku">新宿</option>
<option value="shibuya">渋谷</option>
</select>
<button id="searchByAreaBtn">エリアで探す</button>
<p id="result"></p>
JavaScript(位置情報部分だけ抜粋):
useLocationBtn.addEventListener("click", () => {
if (!("geolocation" in navigator)) {
result.textContent = "位置情報に対応していません。エリアを選択してください。";
return;
}
navigator.geolocation.getCurrentPosition(
(pos) => {
// 現在地で検索
},
(err) => {
if (err.code === err.PERMISSION_DENIED) {
result.textContent = "位置情報は拒否されました。エリアを選択して検索できます。";
} else {
result.textContent = "位置情報を取得できませんでした。エリアを選択してください。";
}
}
);
});
JavaScriptここでのポイントは、
「拒否されたときのメッセージが“行き止まり”になっていない」 ことです。
「ダメでした」で終わらせず、
「だからこうしてください」と次の行動を提示します。
原則 4: 「権限の状態」をちゃんと把握して、無駄に聞かない
Permissions API で状態を確認する
ブラウザには navigator.permissions という API があり、
一部の権限について「状態」を確認できます。
状態は大きく分けて 3 つです。
granted(許可済み)denied(拒否済み)prompt(まだ聞いていない/毎回聞く)
例えば、通知の状態を確認するコードはこうです。
if ("permissions" in navigator) {
navigator.permissions
.query({ name: "notifications" })
.then((status) => {
console.log("通知の状態:", status.state);
});
}
JavaScriptこれを使うと、
すでに denied なのに、何度もダイアログを出そうとする
すでに granted なのに、毎回「許可してください」と説明する
といった「しつこい・無駄な挙動」を避けられます。
状態に応じて UI を変える
例えば、通知の設定画面でこういう分岐ができます。
granted のとき
「通知はオンです。ブラウザの設定からいつでもオフにできます。」
denied のとき
「通知はブラウザ側で拒否されています。変更するにはブラウザの設定を開いてください。」
prompt のとき
「通知をオンにできます。ボタンを押すとブラウザのダイアログが表示されます。」
これをコードにすると、イメージはこんな感じです。
async function setupNotificationUI() {
const msg = document.querySelector("#notifyState");
if (!("Notification" in window)) {
msg.textContent = "このブラウザは通知に対応していません。";
return;
}
if (!("permissions" in navigator)) {
msg.textContent = "通知の状態を確認できませんが、ボタンから設定できます。";
return;
}
const status = await navigator.permissions.query({ name: "notifications" });
if (status.state === "granted") {
msg.textContent = "通知はオンになっています。ブラウザの設定から変更できます。";
} else if (status.state === "denied") {
msg.textContent = "通知はブラウザで拒否されています。設定から変更できます。";
} else {
msg.textContent = "通知をオンにできます。ボタンを押すと確認ダイアログが表示されます。";
}
}
JavaScriptこうやって、
「今どういう状態なのか」をユーザーと共有する のも、権限設計の一部です。
原則 5: 「権限を前提にしない」アーキテクチャにする
権限がなくても“壊れない”コードを書く
権限設計で一番やってはいけないのは、
「権限がないとコードが例外で落ちる」状態です。
例えば、位置情報 API がない環境で
いきなり navigator.geolocation.getCurrentPosition を呼ぶと、
エラーになるブラウザもあります。
なので、必ず存在チェックを挟みます。
if (!("geolocation" in navigator)) {
// 非対応のときの処理
} else {
// 対応しているときだけ呼ぶ
}
JavaScript同じように、Notification や navigator.permissions も
存在チェックをしてから使うのが基本です。
権限がない
API 自体がない
ユーザーが拒否した
こういう状況を全部「普通のケース」として扱い、
「そのときどう振る舞うか」を先に決めておく のが、権限管理設計です。
初心者として「権限管理設計」で掴んでほしい感覚
権限管理は、API の使い方を覚えることよりも、
ユーザーとの関係性をどう設計するか、という話に近いです。
必要なときに、必要な権限だけを聞く
用途・範囲・頻度を短く説明してからダイアログを出す
拒否されても成立する UI・フローを用意する
Permissions API などで状態を把握し、無駄に聞かない
権限や API を前提にせず、「ない世界」も普通に動くようにする
あなたがコードを書くときに、
「この権限、本当に今必要?」「拒否されたらどうする?」
と一度でも自問できるようになったら、
それはもう“ただ API を叩く人”から
“権限を設計できるエンジニア”に一歩踏み出しています。
