Oracle Cloud Hangout Cafe Season4 #4「マイクロサービスの認証・認可とJWT」(2021年7月7日開催)

2023年2月17日(金)
川村 修平
連載第2回の今回は、2021年7月7日に開催された「Oracle Cloud Hangout Cafe Season4 #4『マイクロサービスの認証・認可と JWT』」の発表内容に基づいて紹介していきます。

クライアントタイプ

OAuth 2.0では、クライアント(=リソースサーバーに対して要求を行うアプリケーション)を以下のように分類しています。

  • コンフィデンシャルクライアント
    • クライアントシークレットを安全に保持できるクライアントを指す
    • 典型的には、サーバーサイドの Web アプリケーションとなる
  • パブリッククライアント
    • クライアントシークレットを安全に保持できないクライアントを指す
    • 典型的には、ネイティブアプリやフロントエンドの JavaScript アプリケーションとなる

グラントタイプ

OAuth 2.0では、アクセストークンの取得方法を4つ定義しています。そして、このアクセストークンの取得方法をグラントフローと呼びます。定義されているグラントフローと簡易的な特徴は以下の通りです。

  • 認可コードグラント
    • OAuth 2.0 の基本フロー
    • 短命な認可コードを用いて、アクセストークンと交換を行うフロー
  • インプリシットグラント
    • 認可コードを用いずに、直接アクセストークンを受け取るフロー
    • SPA(Single Page Application)などを想定したフローだが、現在は非推奨であり、認可コードグラント + PKCE(Proof Key for Code Exchange by OAuth Public Clients)が推奨されている
  • リソースオーナーパスワードクレデンシャルグラント
    • リソースオーナーの ID/Password を受け取り、アクセストークンを発行するフロー
  • クライアントクレデンシャルグラント
    • クライアント認証のみで、アクセストークンを発行するフロー
    • クライアントとリソースオーナーが同一の場合に使用される

以降では、基本フローである認可コードグラントのみを説明します(その他のフローは、認可コードグラントから引き算して考えると分かりやすいと思います)。

認可コードグラントによるアクセストークンの取得フロー

下図が認可コードグラントの全体フローになります。

ポイントとなる箇所を見ていきましょう。まずは、図中の認可リクエストについてです。認可サーバーから提供されている認可エンドポイントに対して、クライアントがリダイレクトを利用してリソースオーナーを導きます。この時のリクエストは典型的に以下のようになります。

GET /authorize // 認可エンドポイント
?client_id=2b0c89ed177d42858d48747f97f03b56 // OAuth 2.0 のクライアント ID
&response_type=code // OAuth 2.0 のグラントタイプを指定 (認可コードグラント)
&redirect_uri=https://client.example.com/cb // クライアントへのリダイレクト URL
&scope=get_list view_photo // 要求するリソースのスコープ
&state=b621b8e1-eeff-487c-9ef3-ab92a178a720 // state(セッション単位で使うランダムな値で、CSRF を防ぐために利用すべき)

図中の認証画面認証情報入力では、認可サーバーに対してリソースオーナーであることの認証が行なわれます(i.e: リソースサーバーに対するアクセス権を許可できる人物なのか? ということの認証)。

そして、権限委譲の確認画面権限委譲の同意では、クライアントがリソースサーバーに対して要求するスコープの確認とその同意が行なわれます(様々な場面で以下のような画面を見たことがあると思います)。

クライアントの資格情報や権限委譲の同意が完了すると、図中の認可レスポンス(認可コード)トークンリクエストのように、認可サーバーは認可リクエスト時に送られてきたリダイレクトURIに対して、リソースオーナーをリダイレクトさせます。このときの認可レスポンスは以下のようになります。

HTTP /1.1 302 Found
Location: https://client.example.com/cb // クライアントへのリダイレクトURL
?code=AQIDBAWaII5T42Sle5s63NpsgFkEt8GzJI… // 認可コード
&state=b621b8e1-eeff-487c-9ef3-ab92a178a720 // state (セッション単位で使うランダムな値で、CSRFを防ぐために利用すべき)

この中に出てくるcode=AQIDBAWaII5T42Sle5s63NpsgFkEt8GzJI…が認可コードです。認可コードとは、クライアントがリソースオーナー(ブラウザ)を介さずにアクセストークンを取得するために用いる一時コードのことで、RFC 6749では有効期限を10分以内にすることが推奨されています。

そして、図中のトークンリクエストのように、クライアントから認可サーバーのトークンエンドポイントに対して、認可コードとアクセストークンを交換するためのリクエストが発行されます。このときの形式は典型的に以下のようになります。

POST /token HTTP /1.1 // トークンエンドポイント
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW… // クライアントの認可サーバーに対する認証(Basic認証)
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code // OAuth 2.0 のグラントタイプを指定 (authorization_codeで固定)
&code=AQIDBAWaII5T42Sle5s63NpsgFkEt8GzJI // 取得した認可コード
&redirect_uri=https://client.example.com/cb //  クライアントへのリダイレクトURL

ここでは、クライアントがアクセストークンを所持しても良いのか? という確認のために認可サーバーに対するクライアントの認証がクライアントID/Secretを用いたBasic認証にて行なわれ、認証後にリクエストに含まれている認可コードとアクセストークンの交換が行われます。クライアントへは、図中のトークンレスポンス(アクセストークン)のようにトークンとそれに関連する情報(有効期限やトークンのタイプ)が返却されます。具体的には、以下のような情報が返却されます。

HTTP /1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  “access_token”: “eyJ4NXQjUzI1NiI6IjdZalN6SVl3SnRb3oKpEH9h6iqjpptaNnLsEG…”, // アクセストークン
  “token_type”: “Bearer”, //クライアントからのリソースアクセスの際のアクセストークンの使い方
  “expires_in”: 3600, // アクセストークンの有効期限
  “refresh_token”: “AQIDBAX6uRDWMb3oKpEH9h6iqjpptaNnLsEG_Qr4 xB7wYQrf…” // リフレッシュトークン
}

このとき、認可サーバーの実装や設定によってはリフレッシュトークンという現在発行されているアクセストークンが無効化、もしくは期限切れとなった際に、新しいアクセストークンを取得するためのコードが含まれる場合もあります。

最後に取得したアクセストークンをリクエストに含め、リソースサーバーにアクセスを行います。この際、リソースサーバー側でアクセストークンの検証が行われます。具体的には「アクセストークンの発行先が誤っていないか」「期待通りの発行元から発行されたアクセストークンなのか」「アクセストークンが有効期限内なのか」「エンドポイントを実行するために十分なスコープを有しているのか」といった検証が行われ、検証の後にリソースアクセスが許可されます。

【OAuth 2.0まとめ】

  • OAuth 2.0 は権限委譲/認可を扱うプロトコルであるRFC 6749にて規定されている
  • まずは、基本のフローである認可コードグラントを理解し、その他のフローは引き算で覚える
  • 誤った使い方/実装をしてしまうと、アプリケーションに脆弱性を埋め込むこととなる
    • (工夫なしに)エンドユーザーの認証目的に使う、など
  • エンドユーザーの認証が目的であれば、後述する OpenID Connect を使用すること

OpenID Connect 1.0

以降では、OpenID Connect 1.0について、主にIDトークンに焦点を当てて説明します。

OAuthとOpenID Connectの関係

まずは、前述したOAuth 2.0とOpenID Connect 1.0の関係を整理します。OpenID Connect Core 1.0 incorporating errata set 1のAbstractに以下のように記載がされています。

OpenID Connect 1.0は、OAuth 2.0プロトコルの上にシンプルなアイデンティティレイヤーを付与したものである。このプロトコルはClientがAuthorization Serverの認証結果に基づいてEnd-Userのアイデンティティを検証可能にする。また同時にEnd-Userの必要最低限のプロフィール情報を、相互運用可能かつRESTfulな形で取得することも可能にする。

つまり、OpenID Connect 1.0とは、OAuth 2.0を認証目的で使うためにIDトークンとProfile APIを追加し拡張したものと言い換えることができるでしょう。

OpenID Connect 1.0のアクター

OAuth 2.0と同様に、まずはアクターを整理します。下図もOAuth 2.0の概要で紹介した例を一般的にしたものです。

各名称と役割は以下に記します。XXX(YYY)のように表記した場合は、XXX: OpenID Connect 1.0 のアクター、YYY: OAuth 2.0 のアクターとします。

  • エンドユーザー(リソースオーナー)
    • OIDC には、リソースの概念が存在しないため単に認証対象のユーザーをエンドユーザーと呼びます
  • リライング・パーティー(クライアント)
    • ID プロバイダが認証した結果を受け取るアプリケーションを指します
    • 認証した結果(ID トークン)を検証することでエンドユーザーを信頼(Rely)し認証連携を行うことから、リライング・パーティーと呼ばれています
  • UserInfo エンドポイント(リソースサーバー)
    • ユーザーの情報を提供する API エンドポイント
  • OpenID プロバイダ(認可サーバー)
    • OP などと略称を見かけた際は、こちらのことを指しています
    • 主に、ID トークンおよびにアクセストークンを発行する役割を持ちます

IDトークン

IDトークンについて整理します。IDトークンはエンドユーザーの認証に必要な項目(Claim)を含んだセキュリティトークンとなっており、「誰が? 誰を? 誰のために?」などの認証イベントの情報を含むように標準化されています。トークンの形式は、前述したJWS形式のJWTやNested JWT(JWSをJWRが含む形式)で表現されます。また、JWT形式のアクセストークンとの大きな違いとしては、発行先が違うことが挙げられるでしょう。

上図は、JWT形式のアクセストークンのペイロード例とIDトークンのペイロード例のとなりますが、見比べてみると以下の違いを確認できます。

  • アクセストークン → リソースサーバーに向けて発行
  • ID トークン → リライング・パーティー(OAuth クライアント)に向けて発行

ペイロード内の標準クレーム

下表は、IDトークンのペイロード内の標準クレームとなります(*: 必須項目)。

claim description
* iss Issue Identifier: ID Token の発行者
* sub Subject Identifier: End-User の識別子
* aud Audience: ID Token の発行対象で、OAuth 2.0 の client_id を含む必要がある
* exp Expiration Time: ID Token の有効期限
* iat Issuer At: JWT の発行時刻
auth_time End-User の認証が発生した時刻。リクエストに max_age が含まれている場合は必須
nonce Client セッションと ID Token を紐づける文字列。リプレイ攻撃防止用のパラメータ
acr Authentication Context Class Reference: 実施された認証処理が満たすAuthentication Context Classを示す
amr Authentication Method References: 認証方法を示す
azp Authorizated Party: ID Token の発行対象である認可された関係者(=OAuth 2.0 の client_id)

これを読み解くと、「IDトークンはiss(=OpenID プロバイダ)がsub(=エンドユーザー)をaud(=リライング・パーティ)のために認証しました」という情報が含まれたトークンであると言えます。それゆえ、リライング・パーティーはこのIDトークンを適切に検証することでOPが認証した結果を信頼し、エンドユーザーの認証連携を行うわけです。

日本オラクル株式会社

Oracle Groundbreaker Advocate
Cloud Solution Engineer

日本オラクルに入社後、ソリューション・アーキテクトとしてクラウドでのアプリケーション開発、クラウドネイティブ技術、ストリーム処理、全文検索、AI 等に関する技術支援や案件支援に従事。現在、クラウドネイティブ技術に関連するコミュニティの運営にも参加中。

Community:
Oracle Cloud Hangout Cafe メンバー (#ochacafe)
CloudNative Days - Observability チーム

連載バックナンバー

仮想化/コンテナ技術解説
第6回

Oracle Cloud Hangout Cafe Season6 #1「Service Mesh がっつり入門!」(2022年9月7日開催)

2023/6/22
連載第6回の今回は、2022年9月7日に開催された「Oracle Cloud Hangout Cafe Season6 #1『Service Mesh がっつり入門!』」の発表内容に基づいて紹介していきます。
仮想化/コンテナ技術解説
第5回

Oracle Cloud Hangout Cafe Season5 #5「実験! カオスエンジニアリング」(2022年5月11日開催)

2023/5/18
連載第5回の今回は、2022年5月11日に開催された「Oracle Cloud Hangout Cafe Season5 #5『実験! カオスエンジニアリング』」の発表内容に基づいて紹介していきます。
仮想化/コンテナ技術解説
第4回

Oracle Cloud Hangout Cafe Season5 #3「Kubernetes のセキュリティ」(2022年3月9日開催)

2023/4/18
連載第4回の今回は、2022年3月9日に開催された「Oracle Cloud Hangout Cafe Season5 #3『Kubernetes のセキュリティ』」の発表内容に基づいて紹介していきます。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています