Oracle Cloud Hangout Cafe Season4 #4「マイクロサービスの認証・認可とJWT」(2021年7月7日開催)
スコープ
OpenID Connectとして定義されているスコープは、下表の通りです(*: 必須項目)。
scope | description |
---|---|
* openid | OAuth or OIDC の判定に使用する。OIDCとしてのレスポンスを取得したい場合は指定必須 |
profile | End-UserのデフォルトプロフィールClaim(name、genderなど)へのアクセス要求に必要 |
email、email_verified Claim へのアクセス要求に必要 | |
address | address Claim へのアクセス要求に必要 |
phone | phone_number、phone_number_verified Claim へのアクセス要求に必要 |
認可コードフローによるトークン(ID/アクセス)の取得フロー
ここでは、OpenID Connect 1.0の基本フローである認可コードフローに関して説明します。下図が全体のフローとなります。
ほぼOAuth 2.0の認可コードグラントと変わらないので、ポイントとなる箇所に絞って見ていきましょう。まずは、図中の認証リクエストについてです。OpenIDプロバイダから提供されている認可エンドポイントに対して、リライング・パーティがリダイレクトを利用してエンドユーザーを導きます。このときのリクエストは、典型的に以下のようになります。
GET /authorize // 認可エンドポイント ?client_id=2b0c89ed177d42858d48747f97f03b56 // OAuth 2.0 のクライアントID &response_type=code // OAuth 2.0 認可コードフロー &redirect_uri=https://client.example.com/cb // クライアントへのリダイレクトURL &scope=openid get_list view_photo // 要求するリソースのスコープ(要: openid) &state=b621b8e1-eeff-487c-9ef3-ab92a178a720 // state(セッション単位で使うランダムな値で、CSRFを防ぐために利用すべき)
OAuth 2.0の認可コードグラントとの重要な差分は、scopeにopenid
が含まれていることです。これにより、OpenIDプロバイダはOpenID ConnectかOAuthとしての動作(レスポンス)を決定します。
図中の認証画面〜認証情報入力では、OpenIDプロバイダに対してエンドユーザーであることの認証が行なわれます。ここでの認証方法はOpenID Connectとして定められているわけではないため、ID/パスワードを用いた認証でも、多要素認証でもFIDO 2のようなパスワードレスな認証方法でも問題ありません。
その他の差分としては、トークンレスポンスとして、アクセストークンに加えIDトークンが返却されることです。具体的なレスポンスは以下のようになります。
HTTP /1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Fragma: no-cache { “access_token”: “eyJ...”, // アクセストークン “expires_in”: 3600, // トークンの有効期限 “id_token”: “eyJ...”, // IDトークン “token_type”: “Bearer” // OAuth 2.0 の Token Type の値 (クライアントからのリソースアクセスの際のアクセストークンの使い方を示す) }
最後に、リライング・パーティーがこのIDトークン(=OpenIDプロバイダがエンドユーザーを認証した情報がまとめられたトークン)を正しく検証することで認証連携を実現します。最低限の検証項目としては以下のようになります。
- 署名の検証
- iss(=トークンの発行者)が利用する OpenID プロバイダと一致するかどうか
- aud(=トークンの発行先)に自身のクライアント ID が含まれているかどうか
- iat(=トークンの発行された日時)、exp(=トークンの有効期限)を用いてトークンが有効かどうか
その他にも、amrを用いて期待した認証方法かどうか等を確認することで、検証をより強化できます。
【OpenID Connect 1.0まとめ】
- OAuth 2.0 を拡張し、認証目的で使用できるようにした仕様である
- まずはOAuth 2.0 を最低限理解することが大切
- OAuth の JWT なアクセストークンと OpenID Connect の ID トークンの違いをしっかりと理解する
- トークンの発行先
- アクセストークン → リソースサーバー
- ID トークン → リライング・パーティ(OAuth クライアント)
- ID トークンがどのような情報を含むように標準化されているのか?
- 「誰が? 誰を? 誰のために?」などといった認証イベントに関する情報が含まれている
- トークンの発行先
Javaで実現するマイクロサービスの認証・認可
今までに取り上げた認証・認可について、Java × マイクロサービス・アーキテクチャならどう実現するのか、という観点で俯瞰します。
Eclipse MicroProfile
まず、Javaにはマイクロサービス・アーキテクチャのためにEnterprise Javaを最適化することを目的としたコミュニティ仕様であるMicroProfileが存在します。これはJava EE 7のサブセットとしてスタートし、マイクロサービスの実装に有効な仕様がいくつか追加されています。2023年2月現在、最新版は5.0です。以下の仕様が含まれています。
Eclipse MicroProfile - JWT Propagation
MicroProfileの中には、JWTを伝搬し各マイクロサービスのエンドポイントに対してRBACを実現するための仕様であるJWT Propagation(以降、MP-JWT)が存在します。個人的には、以下の前提知識があると仕様が理解しやすいのではないかと考えています。
- JSON Web Token(JWT)とその周辺仕様
- OAuth 2.0/OpenID Connect 1.0 の基本的な知識
- 特に、OpenID Connect 1.0 で用いられる ID トークンの標準的なクレームについて
ここまで、JWTやその周辺仕様、OAuth 2.0/OpenID Connect 1.0についての基本的な解説は済んでいるため、以降では本題のJWT Propagationについて解説します。
MicroProfile - JWT Propagation
ここでは、MicroProfile - JWT Propagationで用いられるMP-JWTの形式を解説し、MicroProfile準拠のJava アプリケーションフレームワークであるHelidonと、OracleのIDaaSであるIDCS(Identity Domains)を用いた簡易的な実装例を紹介します。
MP-JWT
MP-JWTは、マイクロサービス間で相互運用可能な認証・認可のために使われるJWTのフォーマットを標準的に定義したものです。最新版は2023年2月現在2.0で、仕様はEclipse MicroProfile Interoperable JWT RBACに記載があります。OpenID ConnectのID トークンの標準的なクレームに加えて、Jakarta(Java) EEのRBAC セキュリティ・モデルに組み込むために必要なクレームが追加されています。
{ "typ": "JWT", "alg": "RS256", "kid": "abc-1234567890" } { "iss": "https://server.example.com", "jti": "a-123", "exp": 1311281970, "iat": 1311280970, "sub": "24400320", "upn": "jdoe@server.example.com", "groups": ["red-group", "green-group", "admin-group", "admin"], }
MP-JWTに含まれる標準的なクレーム(Header)
ヘッダーに含まれる標準クレームは、下表のようになっています(*: 必須項目)。
claim name | description |
---|---|
* alg | MP - JWT の署名に使用される暗号アルゴリズムを識別するためのもの (RS-256 or ES-256 or RSA-OAEP) |
enc | クレーム or 入れ子になった JWT(Nested JWT)を暗号化する場合の暗号アルゴリズムを指定する (A256GCM 固定) |
typ | トークンのフォーマットを指定するもの (JWT 固定) |
kid | 検証キーが JWK の場合、どの鍵が使われたかを指定する |