JavaScript | Web API:位置情報・センサー - 権限管理

JavaScript JavaScript
スポンサーリンク

「権限管理」は“ユーザーの OK がないと触れない領域をどう扱うか”の話

位置情報・カメラ・マイク・通知・センサー系は、
どれも「ユーザーのプライバシーや安全」に直結します。

だからブラウザは、こういう機能を使うときに
必ず「ユーザーの許可(権限)」を挟みます。

位置情報を取りたい
カメラを使いたい
マイクを使いたい

こういうときに、
「どうやって許可をもらい、どうやって拒否と付き合うか」
を考えるのが「権限管理」です。

ここを雑に扱うと、
「なんか怪しいサイト」「勝手に位置取ってそう」と思われて、
ユーザーに即閉じされます。

逆に、ここを丁寧に設計できると、
「ちゃんとしてるサービスだな」と信頼されます。


ブラウザの権限ダイアログが出るタイミングを理解する

Geolocation API を呼ぶとき、裏で何が起きているか

位置情報を例にします。

navigator.geolocation.getCurrentPosition(success, error);
JavaScript

このコードが初めて実行されたとき、
ブラウザはこう動きます。

「このサイトは位置情報を使おうとしている」
「ユーザーに許可を聞かないといけない」
→ 画面上部やアドレスバー付近に「位置情報を許可しますか?」ダイアログを表示

ユーザーが「許可」か「ブロック」を選ぶと、
その結果に応じて successerror が呼ばれます。

ここで重要なのは、
許可ダイアログを出すのはブラウザであって、あなたのコードではない
ということです。

あなたができるのは、

いつ Geolocation API を呼ぶか
呼ぶ前にユーザーにどう説明するか
拒否されたときにどう振る舞うか

を設計することです。


権限の状態は「許可・拒否・まだ聞いてない」の 3 つ

Permissions API で状態を覗いてみる

ブラウザは、サイトごとに「権限の状態」を覚えています。

位置情報なら、ざっくり次の 3 パターンです。

許可済み(granted)
拒否済み(denied)
まだ聞いていない(prompt)

これを JavaScript から確認するのが Permissions API です。

async function checkGeoPermission() {
  if (!("permissions" in navigator)) {
    console.log("Permissions API に対応していません");
    return;
  }

  const status = await navigator.permissions.query({ name: "geolocation" });
  console.log("state:", status.state); // "granted" | "denied" | "prompt"
}

checkGeoPermission();
JavaScript

status.state に入る値が、今の権限状態です。

granted … すでに許可されている
denied … すでに拒否されている
prompt … まだ聞いていない(初回)

この状態によって、
アプリの振る舞いを変えることができます。


状態ごとにどう振る舞うかを設計する

granted(許可済み)のとき

すでに許可されているなら、
素直に位置情報を取りに行けば OK です。

async function startWithPermissionCheck() {
  if (!("permissions" in navigator)) {
    // ない場合は、素直に getCurrentPosition を呼ぶしかない
    startGeolocation();
    return;
  }

  const status = await navigator.permissions.query({ name: "geolocation" });

  if (status.state === "granted") {
    // すでに許可済み → すぐに開始
    startGeolocation();
  } else if (status.state === "prompt") {
    // まだ聞いていない → 事前に説明してから開始
    showExplainThenStart();
  } else if (status.state === "denied") {
    // 拒否済み → 設定変更の案内
    showHowToEnableMessage();
  }
}
JavaScript

ここでのポイントは、
「状態によってユーザーへの声かけを変える」 ことです。

prompt(まだ聞いていない)のとき

初回アクセス時など、まだ権限を聞いていない状態です。

このときにいきなり getCurrentPosition を呼ぶと、
ユーザーから見ると

「ページ開いた瞬間に、いきなり位置情報のダイアログが出た」

という体験になります。

これ、結構怖いです。

なので、理想的にはこうします。

画面に「現在地を使って近くのお店を表示します」などの説明を書く
「現在地を使う」ボタンを置く
ボタンが押されたタイミングで getCurrentPosition を呼ぶ

つまり、
「ユーザーの操作 → ブラウザのダイアログ」
という流れにするのが UX 的に自然です。

denied(拒否済み)のとき

一度「ブロック」されたサイトは、
その後 getCurrentPosition を呼んでも、
毎回 PERMISSION_DENIED エラーになります。

コード側から「もう一回聞いて」とは言えません。
権限の再設定は、ユーザーがブラウザの UI から行う必要があります。

だからこそ、アプリ側はこう振る舞うべきです。

「位置情報がブロックされています」
「ブラウザの設定から、このサイトの位置情報を許可してください」

といった案内を出す。

「何度押してもエラーになるボタン」ではなく、
「なぜダメなのか」「どうすれば直るのか」を伝える UI
にすることが大事です。


位置情報の権限管理をコードと UX で組み立てる例

例: 「現在地で近くの店舗を探す」ボタン

HTML:

<button id="useLocationBtn">現在地から店舗を探す</button>
<div id="message"></div>

JavaScript:

const btn = document.querySelector("#useLocationBtn");
const message = document.querySelector("#message");

async function getPermissionState() {
  if (!("permissions" in navigator)) return null;
  try {
    const status = await navigator.permissions.query({ name: "geolocation" });
    return status.state; // "granted" | "denied" | "prompt"
  } catch {
    return null;
  }
}

function findShopsWithLocation() {
  message.textContent = "現在地を取得中…";

  navigator.geolocation.getCurrentPosition(
    (position) => {
      const c = position.coords;
      message.textContent =
        `緯度: ${c.latitude}, 経度: ${c.longitude} をもとに店舗を検索します(仮)`;
      // ここでサーバーに位置を送って店舗検索などを行う
    },
    (error) => {
      if (error.code === error.PERMISSION_DENIED) {
        message.textContent =
          "位置情報の利用がブロックされています。ブラウザの設定から、このサイトの位置情報を許可してください。";
      } else {
        message.textContent = "位置情報を取得できませんでした: " + error.message;
      }
    },
    {
      enableHighAccuracy: false,
      timeout: 5000,
      maximumAge: 60000
    }
  );
}

btn.addEventListener("click", async () => {
  if (!("geolocation" in navigator)) {
    message.textContent = "このブラウザは位置情報に対応していません。";
    return;
  }

  const state = await getPermissionState();

  if (state === "denied") {
    message.textContent =
      "位置情報の利用がブロックされています。ブラウザの設定から、このサイトの位置情報を許可してください。";
    return;
  }

  if (state === "prompt") {
    message.textContent =
      "「許可」を選ぶと、現在地から近くの店舗を探せます。";
  }

  findShopsWithLocation();
});
JavaScript

この例でやっていることを整理すると、

権限状態を事前に確認する
denied のときは、無理に API を叩かず、設定変更の案内を出す
prompt のときは、「これからダイアログが出ますよ」と一言説明してから呼ぶ
実際の位置取得は、ユーザーのボタンクリックをトリガーにする

という「コード+UX」の設計になっています。


HTTPS と「安全なコンテキスト」という前提

なぜ HTTP だと権限系 API が動かないのか

位置情報・カメラ・マイク・センサー系の多くは、
「安全なコンテキスト(secure context)」 でしか動きません。

ざっくり言うと、

https:// で配信されているページ
http://localhost(開発用の特例)

のどちらかでないと、
navigator.geolocation 自体が使えなかったり、
権限ダイアログが出なかったりします。

理由はシンプルで、

通信が暗号化されていない状態で
位置情報やカメラ映像を扱うのは危険すぎる

からです。

なので、「権限管理」を考えるときは、
「本番は必ず HTTPS」 を前提にしてください。

開発中は http://localhost:3000 などで試し、
本番に上げるときは必ず HTTPS 化する、という流れが基本です。


「権限管理」で本当に大事なのは“人間側の感情”を想像すること

技術的には、

Permissions API で状態を確認できる
getCurrentPosition でダイアログが出る
denied のときは PERMISSION_DENIED になる

といった話で終わりです。

でも、本当に大事なのはその先です。

ユーザーは、
「いきなり位置情報を要求してくるサイト」を怖がります。
「拒否したら何もできないアプリ」にイラッとします。
「なぜ位置が必要なのか説明してくれるサービス」に安心します。

だからこそ、権限管理は

いつ・なぜ・どのように権限を求めるか
拒否されたときに、どうやって代替手段や説明を出すか
権限がなくても、どこまで使えるようにしておくか

まで含めて考える必要があります。


初心者として「権限管理」で掴んでほしいこと

位置情報・センサー系の権限管理の本質は、
「ブラウザのダイアログ任せにせず、ユーザーとのコミュニケーションとして設計する」
ということです。

そのうえで、まずこの感覚を持っておいてください。

権限には「許可・拒否・まだ聞いてない」の 3 状態がある
Permissions API で状態を見て、振る舞いを変えられる
権限を求める前に「なぜ必要か」を UI で説明するのが大事
拒否されたときは、設定変更の案内や代替手段を用意する

コードを書くのは簡単です。
でも、「人にどう見えるか」まで含めて設計できると、
あなたのアプリは一段上の信頼感を持つようになります。

権限管理は、
技術と人間の感情のちょうど境目にあるテーマ です。
ここを丁寧に扱えるエンジニアは、間違いなく強いです。

タイトルとURLをコピーしました