すでに直面している課題もあるでしょう。アプリのログインは動作し、Google、Microsoft、またはメールでのサインインが可能で、API がトークンを受け取れる状態です。しかし、そこから本質的な問いが始まります。「このユーザーは別のアカウントの請求書を見ることができてしまわないか?」「デスクトップアプリはアクセストークンをローカルにキャッシュすべきか?」「Capacitor ビルドにおいて、WebView とネイティブ層の間で状態を漏洩させずに、どのように同意(consent)を処理すべきか?」
アプリ認可は、単なる「チェック項目」ではありません。製品への信頼性、インシデント対応、そして App Store の審査結果にまで影響を与える重要な要素です。クロスプラットフォームアプリ、特に Capacitor や Electron を使用する場合、難しいのは認可の概念を理解することではなく、ブラウザの前提が通用しない場所、プラットフォームごとにセキュアストレージの挙動が異なる場所、そしてクライアント側のショートカットがサーバー側のリスクにつながる場所で、いかに正しく実装するかです。
目次
- アプリ認可の真の意味
- 認可を構成する要素
- 一般的な認可モデルとプロトコル
- OAuth 2.0 フローの仕組み
- セキュリティ脅威と必須のベストプラクティス
- Capacitor と Electron の実装パターン
- 安全なアプリ認可への道
アプリ認可の真の意味とは
ユーザーがアプリをインストールし、「Googleで続行」をタップしてログインに成功したとします。すると次に、連絡先やカレンダーへのアクセスを許可するか尋ねる同意画面が表示されます。この瞬間には、アクセスの物語の両側面が含まれています。サインインは「本人確認」を行い、同意画面は「本人確認後にアプリができること」を定義します。
この違いを混同してしまうチームは今でも少なくありません。 認証 ユーザーが誰であるかを証明します。 認可(Authorization) ユーザー、セッション、またはアプリが何にアクセスできるかを決定します。IDは建物への入場を許可し、鍵はどのドアを開けられるかを決定するものです。
アプリ開発において、この違いは極めて重要です。多くのチームはログインフローのセキュリティは固めますが、その後の設計が不十分になりがちだからです。トークンを過信したり、サーバーサイドでの権限チェックを怠ったり、本来ポリシーで管理すべきアクセスルールをクライアント側に任せてしまったりします。その結果、「ユーザーがログインしている」状態が、いつの間にか「ユーザーが過剰なアクセス権を持っている」状態へと変質してしまうのです。
認可とは、信頼を具体的な形にするプロセスです。ユーザーは単にアプリが自分を認識していることだけを求めているのではありません。自分が許可したものだけにアクセスされることを求めているのです。
整理すべき3つの登場人物がいます:
- ユーザー: サインインし、同意を与える主体。
- アプリケーション: ユーザーに代わってアクセスをリクエストする主体。
- リソース所有者、または API: データを保護し、決定を適用する主体。
実際には、アプリの認可はMFAのような強力なアクセス制御とも密接に関連しています。2023年1月時点で、 世界中のユーザーの約66%がMFAを利用しており、、および 2024年のJumpCloudの調査では、1,000人以上のSME ITプロフェッショナルの83%が、すべての社内リソースへのアクセスにMFAを必須としています。 によると JumpCloudのMFA統計まとめこれは認可(Authorization)に代わるものではありませんが、そもそも誰がアクセス権を要求できるかという基準を引き上げます。
チームでロール、スコープ、委譲アクセスを整理しているなら、こちらのガイドが役立ちます: Capacitor アプリにおけるRBACを用いたOTAアップデートの保護方法 ここで紹介した実装の選択肢を補完する内容となっています。
認可を構成する要素
認可を「トークンの中にある魔法」として扱うのをやめれば、理解はぐっと簡単になります。より適切なメンタルモデルは「ホテル」です。
ホテルのカードキーは、優れたメンタルモデルです
宿泊客がフロントに行き、身分証を提示します。ホテルは本人確認を行い、宿泊記録を作成してカードキーを発行します。そのカードは、ドアを開けるたびに宿泊客が誰であるかを証明するものではありません。限られた期間内に、特定の場所にアクセスできる権限を保持しているのです。
あなたのアプリも、これと同じ仕組みで動作します。

重要な点は、カードそのものがポリシーではないということです。カードはポリシーを反映しているに過ぎません。ドア側には、そのカードで特定のロックを開けてよいかをチェックするシステムが必要です。ソフトウェアにおいては、それが API ゲートウェイ、バックエンドのミドルウェア、ポリシーエンジン、あるいはサービスレベルの認可レイヤーにあたります。
実際のシステムで重要な用語
プリンシパル
アクセスを要求する主体。通常はユーザーですが、デバイス、バックグラウンドジョブ、またはサービスアカウントの場合もあります。
リソース
保護対象となるもの。プロジェクト、請求書、管理用ルート、ファイル、API エンドポイント、またはデータベース内の単一のレコードなど。
スコープ
要求されているアクションの集合。プロフィールの読み取り、ファイルのアップロード、請求管理など。スコープは限定的で分かりやすいものであるべきです。
同意
要求されたアクセスレベルに対するユーザーの承認。優れた同意画面はリクエスト内容を明確に伝えますが、不適切なものはすべてへのアクセスを要求してしまいます。
アクセストークン
認可成功後にクライアントがリソースサーバーに提示する認証情報。機密データとして扱う必要があります。
実装上のミスの多くは、「ユーザーがトークンを持っているなら、アクセスを許可する」という一つの思い込みから生まれます。これは本番環境では通用しません。トークンが有効であっても、現在の操作、テナント、環境、またはリソースに対して不適切な場合があります。
モバイルやデスクトップの開発チームにとって、トークンの扱いは細心の注意を払うべき領域です。なぜなら、ストレージの設計は、意図的かどうかにかかわらず認可システムの一部となるからです。クライアント側でアクセス用アーティファクトを不適切に保存してしまうと、後からどれほど優れたポリシーを設計しても防ぎきれません。そのため、リリース前にこちらの モバイル開発者のためのセキュアなトークン保存に関するガイド をぜひ確認しておいてください。
鉄則はシンプルです。認証、同意、トークン発行、そしてサーバーサイドでの強制適用を、頭の中でも code の設計でも明確に分けて考えてください。これらを混同してしまうチームは、結局のところ、全く異なるレイヤーで権限周りのバグに悩まされることになります。
一般的な認可モデルとプロトコル
チームが「OAuthを使っている」と言うとき、実際には複数の異なる概念を同時に指していることがよくあります。それが混乱の原因の一つです。プロトコルと認可モデルは、それぞれ異なる問題を解決するためのものです。
プロトコルはやり取りを担う
OAuth 2.0 は、主に委譲された認可(delegated authorization)に関するものです。ユーザーのパスワードを直接扱うことなく、アプリがユーザーに代わって操作を行うための権限をどのようにリクエストし、受け取るかを定義します。
OpenID Connect(OIDC)は、OAuth 2.0 の上に構築され、アイデンティティ情報を提供します。実用的な観点では、OAuth は「このアプリに何ができるか」に答え、OIDC は「誰がサインインしたか」を明らかにします。
この違いは、Capacitor や Electron アプリにおいて非常に重要です。なぜなら、アクセストークンが必要な場所に ID トークンを使ってしまったり、サインインに成功したからといって API がすべての後続アクションを許可すべきだと誤認したりすることが、多くのバグの起点になるからです。本来、そうあるべきではありません。
これをハイブリッドアプリに組み込む場合、ステップバイステップの Capacitor アプリ向け OAuth2 実装ガイド のようなリソースがあれば、回避可能なフローのミスを大幅に防げます。
モデルは判定ロジックを担う
自社システム内でも、アクセスを許可するかどうかを判断するルールが必要です。そこで登場するのが RBAC と ABAC です。
ロールベースアクセス制御 (RBAC) は、権限を admin、editor、support agent、viewer といったロールに紐付けます。理解しやすく、監査も可能で、比較的安定しているため一般的です。 BrightSec による安全な認証と認可に関する解説によれば, RBACは、きめ細かな権限管理を実現するための業界標準のメカニズムです。また、階層的なロール構造と定期的な権限監査を伴うRBACの実装は 、エンタープライズ環境におけるセキュリティインシデントを最大40%削減できることが示されています.
属性ベースのアクセス制御 (ABAC) は、ロールだけでなく属性を用いて判断を下します。これには、部署、デバイスの状態、レコードの所有権、アカウントティア、地域、リクエスト時刻、あるいはセッションがMFAを通過したかなどが含まれます。ABACはより表現力が高い反面、ポリシーのドキュメント化を怠ると、中身が不透明になりやすい側面もあります。
実践ルール: 製品の権限設定が安定しており、人間が理解しやすい場合はRBACから始めましょう。コンテキストによって判断が変わる場合に、ABACを追加するのが最適です。
RBACとABACの比較
| 基準 | ロールベースのアクセス制御 (RBAC) | 属性ベースのアクセス制御 (ABAC) |
|---|---|---|
| コアとなる考え方 | ロールによるアクセス制御 | 属性によるアクセス制御 |
| 最適なプラン | 社内ツール、ダッシュボード、管理パネル | マルチテナントアプリ、規制対象のワークフロー、コンテキストに応じたアクセス |
| 推論の容易さ | チームや監査人が理解しやすい | 柔軟性は高いが、デバッグが困難 |
| 変更管理 | ロールの追加・変更 | ポリシーや属性ルールの調整 |
| よくある失敗パターン | ロールの乱立 | ポリシーの乱立と隠れたエッジケース |
| 例 | 「サポート担当者はチケットを閲覧できる」 | 「サポート担当者は、勤務時間中に担当地域のアカウントのチケットを閲覧できる」 |
最も高度なモデルを選ぶことに賞品はありません。より良い選択は、チームが一貫して適用できるモデルです。多くのプロダクトのコードベースでは、広範なアクセス境界にはRBACを、所有権やテナント、デバイス状態などの例外には特定の属性を使うことを意味します。
OAuth 2.0 フローの仕組み
OAuthの解説は抽象的すぎるものが少なくありません。実際のアプリでは、特に Capacitor や Electron アプリのようにクライアントシークレットを安全に保持できないパブリッククライアントでは、順序が重要です。
ユーザーがログインをタップすると何が起きるか
ユーザーが Capacitor アプリを開き、「GitHub でログイン」をタップします。アプリは PKCE の code verifier と、そこから派生した code challenge を作成し、システムブラウザまたはセキュアなブラウザタブで認可サーバーへユーザーを送ります。アプリは、自身が開始したリクエストへのレスポンスかを検証できるよう、state も含めます。

認可サーバーでは、必要に応じてユーザーがサインインし、要求されたアクセスを承認します。その後、サーバーは、直接使える長期間有効な認証情報ではなく、認可 code を付けてリダイレクトで戻します。アプリは、設定済みのリダイレクトURI経由でその code を受け取ります。
次に、アプリは code をトークンと交換します。このプロセスでは PKCE が極めて重要です。アプリは元の code verifier を、認可 code と共に送信します。サーバーはこれを事前に送られた code challenge と照合します。一致すればトークン交換は成功します。もし誰かが code を傍受しても verifier がなければ、交換は失敗します。
だからこそ、ネイティブおよびハイブリッドクライアントには PKCE が不可欠なのです。これらのアプリはパブリッククライアントに該当します。攻撃者が bundle を調査したり、code のパスをリバースエンジニアリングしたり、ローカルの状態を改ざんしたりできることを想定すべきです。PKCE は、リダイレクトベースのフローにおける最も一般的なリスクの一つを軽減します。
code でこのシーケンスを実装する前に、視覚的に復習しておきたい場合はこちらを参考にしてください:
クロスプラットフォームアプリがよくつまずくところ
プロトコル自体は単純ですが、実装はそう簡単ではないことがよくあります。
Capacitor アプリが失敗する原因は、通常以下のいずれかです:
- サインインに埋め込み Webview を使用している システムブラウザの代わりに Webview を使うと、期待されるセキュリティ境界が損なわれ、Cookie の挙動が不安定になる可能性があります。
- リダイレクト状態の消失 ブラウザからネイティブシェルへアプリが復帰する際に発生します。
- トークンをブラウザのストレージにそのまま保存している プロジェクトが Web アプリとして始まり、モバイル向けのストレージ設計が後回しになっているためです。
Electronアプリには特有の課題があります。レンダラープロセスに認証ロジックを過剰に任せたり、厳格な境界なしにIPC経由でトークンを公開したり、デスクトップアプリを信頼できる環境だと誤認したりするチームが見受けられます。しかし、パッケージ化されたデスクトップアプリであっても、常に「クライアントは攻撃対象になり得る」というマインドセットが必要です。
リフレッシュの挙動についても、慎重な設計が求められます。アクセストークンには有効期限を設け、セッションはクリーンに復旧できるようにし、リフレッシュロジックが複数の並行リクエスト間でレースコンディションを引き起こさないようにすべきです。 セキュアなトークンリフレッシュフローのガイド は、リトライループやセッション切れの混乱を避けて、その部分を構築するための確かなリファレンスとなります。
実装において、ある一つの習慣が非常に役立ちます。OAuthのハンドシェイクは、入出力が明確な小さな認証モジュール内に隔離してください。リダイレクト処理、トークン解析、リフレッシュロジックを、コンポーネントやフック、あるいはあちこちのネットワークユーティリティに分散させてはいけません。
セキュリティ脅威と必須のベストプラクティス
code のレビューでは、認可に関するバグは劇的なものとしては現れにくいものです。一見すると「利便性」のように見えます。ここでのスコープの広すぎ、あちらでのトークンのキャッシュ、UIでボタンを隠していることによるサーバーチェックの欠落。しかし、アプリがリリースされると、それらの近道がそのまま攻撃対象(アタックサーフェス)となります。
繰り返し見られる失敗
モバイルのエコシステムは、重要な警告を発しています。 DeepStrikeのモバイルセキュリティ統計では、, テストされたモバイルアプリの95%が、認証および認可に関連するOWASP MASVSコントロールの少なくとも1つに失敗しています。、および 分析されたモバイルアプリの85%にセキュリティ上の欠陥が見つかりましたセキュリティマーケティングのあらゆる主張を鵜呑みにする必要はありません。重要なのは本質を見極めることです。認可(Authorization)のミスはよくある問題です。

よくあるパターンは以下の通りです:
- トークンの漏洩 不安全なストレージ、ログ、クラッシュレポート、またはレンダラーからアクセス可能な状態からの漏洩。
- 過剰なスコープ 時間の経過とともに権限を更新するよりも、最初からすべてを要求する方が簡単だからです。
- クライアントサイドでの強制 アプリ側で未承認のアクションを隠しているだけで、API はそれを受け入れてしまうケース。
- リプレイ攻撃およびリダイレクト攻撃 State、PKCE、またはリダイレクトURIの検証が不十分な場合に発生します。
- 権限のドリフト 定期的なレビューなしに、チームがロールや例外を追加し続けた結果として発生します。
バックエンドですべての保護されたアクションに対して認可を確認していなければ、それはアプリの認可ではなく、単なるUI上のヒントに過ぎません。
信頼できる実践的なチェックリスト
最小権限の原則を、後回しの整理作業ではなく、デフォルトとして適用してください。実際のプロジェクトにおいては、各トークンの権限範囲、生存範囲、および有効期間を最小限に抑えることを意味します。
- スコープを絞ってリクエストする: ユーザーが現在使用している機能に必要な権限のみを要求してください。アプリが同意の取得を遅延できる場合は、そうすべきです。
- サーバー側で強制する: クライアントは信頼できないものとして扱ってください。ボタン、ルート、非表示の画面はセキュリティ境界ではありません。
- プラットフォームのセキュアストレージを使用する: モバイルでは、プレーンなlocal storageではなく、プラグインを通じてネイティブのkeychainやkeystoreにアクセスしてください。デスクトップでは、機密情報をレンダラーが容易にアクセスできない場所に保持してください。
- 状態の検証とリダイレクト処理: 認証レスポンスは、アプリが開始したリクエストと一致している必要があります。
- 有効期限を短く設定し、慎重にリフレッシュする: 短寿命のアクセストークンは、漏洩時の被害を最小限に抑えます。リフレッシュロジックは、クリーンにローテーションし、失敗時にはアクセスを遮断(fail closed)するように設計すべきです。
- 必要に応じて失効させる: セッションの終了やインシデント対応には、トークンの無効化や強制的な再認証を行う機能を含める必要があります。
- 入力を検証し、通信を保護する: HTTPS、適切な場所での証明書ピンニング、そして入力バリデーションはすべて重要です。認証は、他の脆弱性を突くことでバイパスされる可能性があるからです。
ストア経由でアプリを配信するチームにとって、認証設計は API の露出やコンプライアンス審査とも密接に関わります。この アプリストアのコンプライアンスに向けた API セキュリティ基準のまとめは、 認証チェックリストの補足として役立ちます。
最後に見落としがちな点です。最小権限の原則は内部ツールにも適用されます。管理パネル、サポートコンソール、ステージングアプリは、最も機密性の高い操作を伴うことが多いにもかかわらず、社内で最も制御が緩くなりがちです。
Capacitor と Electron の実装パターン
アプリを単なる「追加パッケージ付きのブラウザ」だと考えるのをやめれば、クロスプラットフォームアプリの認可はもっと簡単になります。Capacitor も Electron も、ネイティブストレージ、プロセス境界、リダイレクト処理を考慮したパターンが必要です。

機能する Capacitor のパターン
Capacitor の場合、PKCE 対応のシステムブラウザ OAuth と、適切なディープリンクまたは App Link のコールバックをサポートするプラグインや認証ライブラリを使用してください。例えば、 capacitor-oauth2 などを使えば、グルー code を大幅に削減できますが、トークンの保存方法とリフレッシュの挙動は明示的に管理し続ける必要があります。
実用的な構成例は以下の通りです:
- 認証コーディネーター: ログインを開始し、状態を追跡し、コールバックを処理します。
- トークンサービス: ブラウザストレージではなく、ネイティブのセキュアストレージにトークンを保存します。
- API クライアント: アクセストークンを付与し、リフレッシュ時に1回リトライし、回復不能な失敗時には強制サインアウトします。
- ポリシーを考慮したバックエンド: トークンのクレームをサーバー側の認可チェックにマッピングします。
ハイブリッドアプリのライフサイクルに適合するセッション管理ツールも必要です。そのレイヤーの選択肢を検討しているなら、 Capacitor セキュアなセッション管理用プラグイン 各アプローチを比較するのに適しています。
最小限のトークンリフレッシュの擬似コードは以下の通りです:
async function authorizedFetch(request) {
let token = await tokenStore.getAccessToken()
let response = await api(request, token)
if (response.status !== 401) return response
const refreshed = await auth.refresh()
if (!refreshed) {
await auth.signOut()
throw new Error('Session expired')
}
token = await tokenStore.getAccessToken()
return api(request, token)
}
重要なのは構文ではなく、リフレッシュ処理を中央集約化することです。各画面が独自のセッション挙動を持たないようにします。
注意が必要なElectronのパターン
Electronにはより厳格な境界が必要です。可能な限り、トークンの交換とセキュアなストレージはメインプロセスで管理してください。レンダラーに生のトークンを渡して挙動を期待するのではなく、限定的なIPCメソッドのみを公開するようにします。
デスクトップアプリはモバイルアプリよりも制御しやすいと感じがちですが、その感覚を「保証」ではなく「リスク」として捉えてください。
避けるべきショートカット:
- レンダラーからアクセス可能なlocal storageにトークンを保存しないこと 可能な限り避けてください。
- すべてのウィンドウに広範な認証コンテキストを共有させないこと。 ウィンドウの用途やセッションの有効期限を確認せずに共有するのは危険です。
- preloadスクリプトを過信しないこと。 プロセス分離や明示的な API の境界の代わりにはなりません。
運用の可視性も重要です。Splunkによる アプリケーションセキュリティ要件の概要によると、アプリケーションの認可は、継続的なアクティビティ監視およびロギングと統合されるべきです。また、同資料のベンチマークデータでは、認可イベントを積極的にログに記録し監視している組織は、 不正アクセスの試みの95%を15分以内に検知できています。実際には、拒否されたアクション、トークンのリフレッシュ失敗、ロールの変更、同意の取り消し、および異常なリソースアクセスパターンをログに記録することを意味します。
リリースプロセスにハイブリッドアプリのアップデートが含まれる場合、このエコシステムにおける一つの選択肢は Capgo, Capacitor や Electron アプリ向けに署名付きの Live Update を提供します。これは認証を自動で実装するものではありませんが、認証ロジックやリダイレクト処理、セッションの code に緊急の修正が必要になった際、どれだけ迅速に修正をリリースできるかに大きく影響します。
セキュアなアプリ認証への道
優れたアプリ認証は、単一の決定で決まるものではありません。一連の決定がすべて整合していなければなりません。ユーザーを正しく認証し、必要なアクセス権のみを要求し、OAuth 2.0 + PKCE のような安全なフローでトークンを交換し、シークレットを適切な場所に保存し、毎回サーバー側で権限を強制することです。
Capacitor や Electron チームにとって最も重要なのは、境界部分での規律です。ブラウザ時代のショートカットは、ネイティブストレージ、ディープリンク、デスクトップのプロセス境界、あるいはアプリの審査要件に直面すると通用しません。トラブルを回避できているチームは、特別なことをしているわけではありません。スコープ、セッション管理、サーバーサイドのチェック、そして監査可能性について、一貫した対応ができているだけなのです。
現在の認証設定が複雑に感じられるなら、それは自然なことです。まずは、境界を一つずつ固めることから始めましょう。フローを修正し、ストレージを修正し、アクセスルールを UI から切り離します。そして、不適切なアクセス要求があった際に検知できるログを追加しましょう。
そうすることで、セキュアなアプリ認証は管理可能なものになります。理論上の話ではなく、code においてより安全なものになるのです。
Capgo を使えば、ストアの審査を待たずに Capacitor や Electron アプリへ修正を配信できます。認証フロー、トークン管理、セッションのバグを迅速に修正する必要がある場面で非常に強力です。クロスプラットフォームでのリリース管理、ターゲットを絞ったロールアウト、ロールバック機能の強化を目指すなら、 Capgo アプリの認証スタックと併せて検討する価値があります。