まくろぐ
更新: / 作成:

認証・認可の仕組みである OAuth や OIDC (OpenID Connect) まわりは何かと説明する機会が多いので、ポイントを絞ってまとめておきます。 これを理解すればわかった気になれるという説明です。 サービスの全体構成を考えるときなどに使える一枚絵ができるといいなぁ。

認証と認可

基本をおさらい。

認証 (Authentication)
ユーザーが誰であるかを確認するプロセスです。 例えば、ユーザー名とパスワードを使ってログインすることが認証の一例です。
認可 (Authorization)
認証されたユーザーが、クライアント(アプリケーション)に対して、どのユーザーリソースにアクセスできるか、どの操作を実行できるかを決定するプロセスです。 勘違いされがちですが、認可の対象はあくまでクライアント(アプリケーション)であり、ユーザーではありません。 ユーザーが、第三者のアプリに対して自分のデータにアクセスしてよいと許可を与えることです。

OAuth と OIDC

OAuth

2012 年に RFC 6749 として OAuth 2.0 が公開されました。 「RFC むなしく」と覚えます(覚えなくてもよいけど)。

OAuth は認可のための HTTP ベースのフレームワーク で、ユーザーのリソース(データ)へのアクセス権を、特定のアプリケーションに付与するための仕組みを提供します。

例えば、Google カレンダーの情報を扱うアプリを作ったとすると、ユーザーは Google の認可画面で「このアプリに自分のカレンダーデータへのアクセスを許可する」ことに同意します。 構図としては、ユーザー自体にアクセス権限を与えているわけではなく、アプリ(OAuth 用語ではクライアント)にリソースへのアクセス権を委譲している という点を理解してください(混同しがちなところ)。 結果として、そのアプリはユーザーの代わりに Google Calendar API などを呼び出すためのアクセストークンを取得し、許可された範囲(スコープ)内でデータにアクセスできるようになります。

OIDC (OpenID Connect)

2014 年に OpenID Foundation が OpenID Connect Core 1.0 を公開しました。

OAuth 2.0 は本来「認可(authorization)」のためのフレームワークであり、ユーザー認証(authentication)を直接目的とした仕様ではありません。 しかし実際には、アクセストークンやスコープを流用して「ログイン」のような用途で使われることが多くありました。

OpenID Connect(OIDC)は、この「OAuth 2.0 の上で動作する認証レイヤー」 として定義された仕様です。 具体的には、ID トークンという JWT 形式のトークンや、ユーザー情報(クレーム)を取得するためのエンドポイント (/userinfo)、およびクレームの標準的な定義などを追加しています。

OpenID Connect (OIDC) は OAuth 2.0 を拡張する形で設計されているため、OIDC を利用するということは、基盤として OAuth 2.0 も同時に利用していることになります。

登場人物と用語

下記は OAuth 2.0 の文脈で出てくる 4 つの構成要素です。 RFC 6749 上では Role(役割)として定義されています。 設計議論をするときは、左端の「通称」を使って話しておけば伝わるはずです。

通称OAuth 用語OIDC 用語説明
ユーザーResource Owner / End UserUser / Personアプリの利用者。参照しようとしているユーザーリソースの持ち主なので、OAuth 2.0 では Resource Owner と呼んでいます。
リソースサーバーResource ServerResource Server / UserInfo Endpointユーザーのデータを保持しているサーバー。アクセスには認可サーバー (IdP) が発行するアクセストークン※1が必要。OIDC ではユーザー情報を提供するためのエンドポイントも定義しています。
クライアントClientRelying Party (RP) / Clientアクセストークンを使ってユーザーのリソースにアクセスするアプリ。クラウド上で動作するロジックでも、クライアントと呼ぶのでお気をつけください。あくまで、リソースサーバーに対してのクライアントということを表現しています。
認可サーバー (IdP: Identity Provider)Authorization ServerOpenID Provider (OP) / IdPユーザー認証機能の提供や、アクセストークン※1の発行を行うサーバー。実体としてはリソースサーバーと同じサーバーでもよい。

※1 … 実際には複数のトークンがありますが、ここでの説明では代表的なトークンである「アクセストークン」という単語でまとめています。

OAuth による認可フローの種類

認可フローというのは、クライアント(アプリ)が IdP からアクセストークンを取得するまでの手順のことです。 OAuth 2.0 (RFC 6749) では、下記の 4 つの認可フローを定義しています。 一番重要かつ理解しておきたいのは、1 つ目の 認可コード を使うフローです。

認可フローの種類説明
認可コード
(Authorization Code Grant)
IdP が生成したランダムな「認可コード」を使って各種トークンを取得するフローです。クライアント(アプリ)はブラウザからのリダイレクトによって認可コードを受け取り、それを使ってアクセストークンを取得します。OAuth の中で 最も一般的かつ重要なフロー です。
インプリシット
(Implicit Grant)
認可エンドポイントが(認可コードではなく)アクセストークン自体を発行するフローです。リダイレクト先の URL にアクセストークンが付加されてしまう(クライアントではなくユーザーにも見えてしまう)というセキュリティ上の懸念があり、現在は 非推奨 (deprecated) となっています。
リソースオーナー・パスワード・クレデンシャルズ
(Resource Owner Password Credentials Grant)
ユーザー (Resource Owner) が、自らの認証情報(ユーザー名・パスワードなど)をクライアントに直接入力し、クライアントがそれをそのまま認可サーバーに送ってトークンを取得するフローです。第三者のクライアント(アプリ)にパスワードが渡されてしまうという問題があり、現在は 非推奨 (deprecated) となっています。
クライアント・クレデンシャルズ
(Client Credentials Grant)
クライアント(アプリ)が自らのクレデンシャル(例:client_id + client_secret、あるいは API キーなど)を使って、自分自身の名義でアクセストークンを取得するフローです。ユーザー (Resource Owner) の認証・同意は介さず、トークンは「ユーザーの権限」ではなく「クライアント自体の権限」を表します。主にバックエンドサーバー間での連携 に使われます。
☝️ ワンポイント 正確には OAuth 2.0 では Authorization Grant Type(認可付与タイプ) という言葉で上記の分類を行っています。 アクセストークンを取得するための認可をどのように与えるかという観点で分けているんですね。 認可フロー という言葉は同じものを指していると考えてよいです。

認可コードフロー (Authorization Code Flow)

認可コードフロー (Authorization Code Flow) は、OAuth 2.0 における最も一般的な認可フローであり、ほぼすべての登場人物が含まれています。 そのため、この認可コードフローの仕組みを理解できれば、「OAuth 知ってるよ」と言っていいと思います。 ポイントは、クライアント(アプリ)がブラウザ経由で一時的な「認可コード」を受け取って、そのコードを使って最終的なアクセストークンを取得する という流れです。

/p/ntbwx26/img-auth-code-flow.drawio.svg
図: 認可コードフローの概念図(最重要)

[認可リクエスト]まず、ユーザーはアプリからの指示によりブラウザ(Chromeなど)を起動し、サインインと認可を行います。 例えば、自分の Google カレンダーの情報を第三者のアプリに参照させてもよいよ、という同意をします。 このとき使うサインイン画面は、認可サーバー(Googleなど)が提供するものを使うのが一般的です(ユーザーは見慣れた画面でサインインできます)。
ユーザーによる認可が終わると、その証明である「認可コード」が HTTP のリダイレクトレスポンスとしてブラウザに返されます(URL のクエリパラメータに code が付加される)。
リダイレクト URL はクライアントを起動するものになっているため、クライアントは「認可コード」を受け取ることができます。
[トークンリクエスト]クライアントは、受け取った「認可コード」を使って、認可サーバーにアクセストークンを要求します。
認可サーバーは、クライアントからのリクエストを検証した上で、アクセストークンを発行します。
クライアントは、取得したアクセストークンを使って、リソースサーバーにアクセスし、ユーザーのデータを取得します。

このような一見まわりくどい構成にすることで、認証・認可処理をブラウザ (User-agent) と認可サーバー (IdP) の間だけで完結することができる ため、ユーザーのパスワードをクライアント(アプリ)に渡す必要がありません(図の①②の部分)。 また、アクセストークンの取得処理がクライアント(アプリ)側に閉じる ため、ブラウザの URL などからトークンが漏れてしまうといったリスクを避けることができます(図の④⑤の部分)。 アクセストークンは基本的にはユーザーにさえ見えず、クライアント(アプリ)だけが参照できます。

つまり、「認証・認可の処理を行う世界」と、「アクセストークンを取得する世界」を分離することにより、セキュリティ上のリスクを低減しているのです。 よく考えられていますね。 ここで勘の良い方は気付いたかもしれませんが、これらの世界をつなぐための「認可コード」という仕組み(図の②③④あたり)に抜け穴はないのかということです。 そこで必要になってくるのが、クライアント認証です。

クライアント認証(クライアントの信頼性を確認する)

/p/ntbwx26/img-malicious-client.drawio.svg
図: 悪意のあるクライアントからのトークンリクエスト

認可サーバー (IdP) は、アクセスしてくるクライアント(アプリ)が正規のクライアントかどうかを検証した上で、アクセストークンを発行する必要があります。 これを「クライアント認証」と呼びます。 クライアントの信頼性を確認してからアクセストークンを発行しないと、悪意のあるクライアントにアクセストークンを渡してしまう危険性があります。 例えば、正規のクライアントのリダイレクト URL を偽装して認可コードを取得し、認可サーバーからアクセストークンを取得する、といった攻撃が考えられます。

クライアントの種類

前述の認可コードフローの図では、クライアント(アプリ)を 1 つの青色の箱でシンプルに表現していますが、実際には、サーバー上で動くアプリであったり、ブラウザ上(クライアントサイド JavaScript)で動くアプリであったりします。 OAuth 2.0 では、クライアントを「資格情報を安全に保持できるか」で次の 2 種類に分けており、それぞれクライアント認証の方法が異なります。

  • コンフィデンシャル・クライアント (Confidential Client)
    • クライアント・シークレットを安全に保持できるクライアント。 一般的にはクラウド上で動作するアプリケーションです(例:サーバーサイドアプリ、BFF (Backend For Frontend)。
  • パブリック・クライアント (Public Client)
    • クライアント・シークレットを安全に保持できないクライアント。 一般的には端末側で動作するアプリケーションです(例:ブラウザ上で動くクライアントサイド JavaScript や、Android/iOS/Windows/macOS/Linux などのネイティブアプリ)。
/p/ntbwx26/img-client-types.drawio.svg
図: いろいろなクライアント構成

コンフィデンシャル・クライアントの検証方法

コンフィデンシャル・クライアントの場合は、クライアント・シークレット を使ったクライアント認証を行います。 簡単にいうと、クライアントと認可サーバー (IdP) の間で共有しておく秘密のパスワードです。 クライアント・シークレットは、認可サーバーの Web フォーム上でコンフィデンシャル・クライアントを登録するときに発行されるのが一般的です(クライアント ID とクライアント・シークレットのペアを生成します)。

サーバーサイドで動作するコンフィデンシャル・クライアントであれば、環境変数などにクライアント・シークレットを登録しておくことができ、認可サーバーとの通信時にそれを送るだけで自分が正規のクライアントであることを証明できます。 具体的には、client_idclient_secret のペアを HTTP のリクエストボディに含めて送信するか、Basic 認証のヘッダー情報として送信します。

/p/ntbwx26/img-confidential-client-valid.drawio.svg
図: コンフィデンシャル・クライアントからのトークンリクエスト

認可サーバーは、コンフィデンシャル・クライアントからのアクセス時には必ずクライアント・シークレットの値を確認することで、正しいクライアントのみにアクセストークンを発行することができます。

パブリック・クライアントの検証方法

一方、クライアントサイド JavaScript (SPA: Single Page Application) や、ネイティブアプリのような端末側で動作するパブリック・クライアントでは、クライアント・シークレットを使って自分が正規のクライアントであることを証明することができません。 なぜなら、クライアント・シークレットを JavaScript コードに埋め込んでしまうと、ブラウザの開発者ツールなどで簡単に参照できてしまい、悪意のあるクライアントに悪用されてしまうため、意味がないからです。

つまり、認可サーバーは、パブリック・クライアントが正規のクライアントだと仮定してアクセストークンを発行するしかありません。 認可コードフローの途中で、何らかの形で「認可コード」が悪意のあるクライアントに傍受されてしまうと、それはすなわちアクセストークンを取得されてしまうことを意味します。 そこで考えられた対応策が、PKCE (Proof Key for Code Exchange)(ピクシー) という OAuth 2.0 の拡張仕様です。

☝️ 認可サーバーへのクライアント登録

認可サーバーにクライアントを登録する際は、クライアントの種類(コンフィデンシャル・クライアントかパブリック・クライアントか)を指定する必要があります。 これは、上記のように、クライアント認証の方法が異なるためです。 例えば、コンフィデンシャル・クライアントとして登録された場合は、認可サーバーはトークンリクエスト時にクライアント・シークレットの確認を行います。 パブリック・クライアントとして登録された場合は、クライアント・シークレットの確認は行わず、PKCE を使った検証を行います。

さらに、認可コードフローで使うためのリダイレクト URI をあらかじめ登録しておく必要がありますが、これは、悪意のあるクライアントに認可コードが転送されてしまうことを防ぐための仕組みです。 しかし、リダイレクト URI の登録だけでは、悪意のあるクライアントが正規のクライアントのリダイレクト URI を偽装して認可コードを取得することを防ぐことはできません。 また、クライアントが Android などのネイティブアプリである場合は、リダイレクト URI に独自のスキームを設定することがあり、リダイレクト時に悪意のあるクライアントが起動してしまう可能性が高くなります(参考:RFC 8252: OAuth 2.0 for Native Apps)。

PKCE (Proof Key for Code Exchange)

パブリック・クライアントを用いた構成では、PKCE (Proof Key for Code Exchange)(ピクシー) という OAuth 2.0 拡張を組み合わせて使うことで、正しいクライアントからのアクセストークン要求であることを判断します。 認可コードフローの中で、悪意のあるクライアントに「認可コード」が途中で傍受されてしまった場合に、アクセストークンの発行を止めるための仕組みです。 2026 年現在ドラフト段階である OAuth 2.1 では、認可コードフローを使うすべてのクライアントに対して PKCE の使用が必須とされています(パブリック・クライアントに限りません)。

PKCE の考え方はシンプルで、「認可リクエストを出したクライアント」と「アクセストークンリクエストを出したクライアント」が同一であることを確認する、というものです。

/p/ntbwx26/img-pkce.drawio.svg
図: PKCE の概念図

正規のクライアントは、認可リクエストを出す前に、自分しか知り得ないランダムな code_verifier を生成して内部で保持しておきます()。 認可リクエストでは、これを SHA-256 でハッシュ化した値 code_challenge を送信します()。 認可サーバーは、認可リクエストを受け取ったときに、code_challenge を保持しておきます() 。 正規のクライアントがアクセストークンリクエストを出すときに、code_verifier を送信します()。 認可サーバーは、受け取った code_verifier をハッシュ化して、保持していた code_challenge と一致するかを検証します()。

これにより、仮に悪意のあるクライアントが「認可コード」を傍受したとしても、code_verifier を知らない限り、アクセストークンを取得することはできません。 現在 OAuth 2.0 でパブリック・クライアントによる認可を行う場合は、この「認可コードフロー + PKCE」という構成が推奨されています。

Device flow(デバイスフロー)

Device Authorization Grant(デバイス認可グラント)、通称「デバイスフロー」 とは、テレビやオーディオ機器などの入力手段が乏しいデバイスで OAuth 2.0 を使った認可を行うための拡張仕様です。 RFC 6749 (OAuth 2.0) では、アプリケーション(クライアント)を使う端末にインストールされたブラウザ (user-agent) を使って認可を行うフローが前提となっていますが、テレビなどのデバイスでは、ブラウザが使えなかったり、ユーザーが文字入力を行うのが困難な場合があります。 デバイスフローでは、ユーザーが別のデバイス(スマートフォンや PC など)を使って認可を行うことができます。 逆に、テレビ側のブラウザは使わず、ユーザーがスマートフォンや PC で認可を行うための URL とコードを表示するだけです。

/p/ntbwx26/img-device-flow.drawio.svg
図: デバイスフローの概念図

デバイスフローでは、認可リクエストを受け付けるエンドポイントとして、Device Authorization Endpoint を定義しています。 トークンリクエストには、通常の認可コードフローと同様の Token Endpoint を使います。

デバイスフローは認可コードフローとは独立したグラント種別(grant_type=urn:ietf:params:oauth:grant-type:device_code)です。 認可コードのリダイレクトを行わず、クライアントがトークンエンドポイントをポーリングする方式のため、PKCE は適用されません。 セキュリティ上の保護は、device_code の秘匿、user_code の短い有効期限、およびポーリングのレート制限によって担保されます。

ネイティブアプリの場合

RFC 8252 では、ネイティブアプリから OAuth 2.0 を使う場合のベストプラクティス を定義しています。 ネイティブアプリというのは、スマホアプリ (iOS/Android) や、PC 用のアプリ (Windows/macOS/Linux) など、端末側の OS 上で動作するアプリケーションのことを指します。 これらのネイティブアプリが認可リクエストを出すときは、アプリ外部の User-Agent(つまり Web ブラウザ)を使う ことになります。

理屈的には、ネイティブアプリに組み込まれた WebView などのブラウザーコンポーネント(Embedded user-agent と呼びます)を使って認可リクエストを出すことも可能ですが、セキュリティ上の懸念があるため推奨されていません。 アプリ内部で直接認可処理を行わず、アプリ外部のブラウザを使用するのには以下のような理由があります。

  • アプリ(クライアント)から認証・認可のやりとりを覗き見られないようにする。
  • アプリの UI 上に悪意のある透明なウィンドウ (iframe) をかぶせられるといった攻撃(クリックジャッキング)を防ぐ。
  • ブラウザにセッション情報が残っていれば、ユーザーは再度ログインすることなく認可を行える (Single Sign-On)。

アプリ外部のブラウザを使うといっても、OS によってはアプリと一体化されているように見せることができます。 例えば、Android では Custom Tabs プロトコルを実装したブラウザ(Chrome など)であれば、In-App Browser Tab としてアプリに組み込む形で認可 UI を表示することができます。

OIDC (OpenID Connect)

その他/補足情報

RFCs

代表的な IdP

  • Amazon Cognito … AWS で提供されている IDaaS
  • Keycloak … オープンソースの IdP
  • Okta … 商用 IDaaS(企業向け)
  • Auth0 … 商用 IDaaS(開発者向け)(2021 年に Okta が買収)
  • Authlete … IdP を実装するための API を提供
更新: / 作成:

2 カ月前に始めた 『エルミナージュ (Elminage) Switch 版』 をやっとクリアしました。 長かったけど面白かったー(100時間はかかってない)。 Wizardry や世界樹の迷宮シリーズが好きな人にはかなりおすすめのダンジョンゲーです。

クリアしたとは書きましたが、実はこれから 2 周目の世界が始まります。 パーティーのレベルを 100 くらいまで上げて、すべての指輪を集め、すべての祭壇で祈りをささげ、すべての公爵を倒したのですが、その状態でエルミナージュの司書のお姉さんに話しかけると、下記のような提案をされて世界を巻き戻されてしまいました。

/p/c33ufkx/img-001.jpg
図: ヒズベルト姉さんからの面白い体験の提案

ワクワクしていると。。。

/p/c33ufkx/img-002.jpg
図: 調子に乗りすぎて神々の世界に近づきすぎたようです

2 周目以降はレア・アイテムの出現率が上がるため、ここからが本番らしいです(3 周目はさらにアップ)。 実は 1 周目のラストダンジョンの深層では、敵があまりも強すぎてレベル 100 前後でもまったく歯が立たない状態でした(たぶん相手は神々)。 その部分は未踏状態で残ってしまったので、2 周目、あるいは 3 周目でリベンジです。

/p/c33ufkx/img-kami.jpg
図: レベチな神たち。なんか白い。出直してくるから待ってろよ!

キャラクターのレベルは 100 とかが限界ではなく、なんと 4 桁まで上がるらしいです。 そこまではさすがに厳しそうですが、もっとレベルを上げないと深層を攻略することはできなさそうなのでがんばります。 世界が巻き戻されたといっても、キャラクターのレベルや装備はそのまま残るので、2 周目以降はイベントを進めていくだけでよさそうです。 各イベントの進め方は 1 周目ですべてメモってあるので、きっとサクサク進められます。

始めたときにも書きましたが、このゲームの作り込みには圧倒されました。 すべてのアイテムとモンスターに設定があって、もはや書籍のようなボリュームです。 なんか 2 周目以降はキャラクター同士で結婚して子供キャラクターを作れるらしい。 ドンダケーッ。

関連記事

更新: / 作成:
/p/wbttd2i/img-dorikin.jpg
図: 30年前のドリキン本+α

車は持っていないし、今後運転する予定もないんだけど、30年くらい前に買って封印されていた土屋圭一氏(ドリキン)の本を今さら読んでます。 最近の若い子はドリキンを知っているのだろうか。 アニメ『頭文字D』にもゲスト声優として出てました。 というかこんな本を買ってたのは絶対に頭文字Dの影響です。

クラッチの仕組みとか、サスチューンのコツとか知ったところで別に役に立たないんだけど、なんか面白いと思ってしまう自分がいます。 カスタマイズというもの全般にロマンを感じているだけなのかもしれない。 最近使ってるレバーレスコントローラーでもキースイッチとかキーキャップを色々変えて遊んでいます。

/p/wbttd2i/img-haute42-b16.jpg
図: カスタマイズはロマンがあって楽しい

ロマンがあるといえば、格ゲーの Guilty Gear シリーズには、どんな行動でもキャンセルできるロマンキャンセルというすごい仕組みがあるんですけど、このネーミングセンスは格ゲー史上でも最高レベルだと思います。 なんの話やねん。

土屋氏はただの面白いおじさんかと思ってたんですが、本の内容はすごくまともですね(←失礼)。 グリップ走行でもアクセルワークだけで曲がり方を変える練習ができるとか、そういった話を聞くと楽しそうだなと思ってしまいます。 車は乗らないんだけどね。 安全なところでレースだけできる世界があったら楽しいだろうなぁ。 グランツーリスモしかないか。 そういえば、実在の日本を走り回れる Forza Horizon 6 というゲームもありますね。 もうすぐ PS5 版が出るので買ってもいいかも。 秋葉原や首都高を走れるらしいですよ。

関連記事

メニュー

まくろぐ
サイトマップまくへのメッセージ