Tagbangers Blog

Keycloak と Spring Boot アプリケーションで OpenID Connect の Authorization Code Flow を確認する

はじめに

本日は、以下のようなシーケンスで OpenID Connect の Authorization Code Flow を確認していこうと思います。

開発済みのプロジェクトのいくつかで Keycloak,OpenID Connect, Spring Cloud Gateway が使われており順に理解していく必要を感じたため、今回は基本的な構成から学んでいきたいと思います。

UserInfo Endpoint(Spring Boot) および IdP(docker container として起動) のサンプルコードは以下のリポジトリに用意しました!

https://github.com/kiyotake-tagbangers/resource-server-for-blog

そもそも、 OpenID Connect とは

OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol.

OpenID Connect(以降 OIDC) のウェブサイト にあるように、認証情報を含むアイデンティティ情報を受け渡しできるように、OAuth 2.0(以降 OAuth) を拡張したものです。OAuth は権限委譲(=認可)のためのフレームワークであり、OAuth をそのまま認証に利用すると脆弱性があります。

参考: 単なる OAuth 2.0 を認証に使うと、車が通れるほどのどでかいセキュリティー・ホールができる

具体的には、悪意のあるクライアントがアクセストークンを取得してしまうと、別の OAuth を利用して認証しているリソースサーバにアクセスできてしまうことが問題のようです。

OIDC では、 OAuth のアクセストークンに加えてJWT形式の ID Token を発行し、こちらにユーザを識別できる情報を入れることで攻撃を防ぎます。(フローの6番の箇所)

OAuth のアクセストークンを発行するのが認可サーバ(Authorization Server)、OIDC の ID Token を発行するのが OpenID Provider(IdP) なのですが、

OIDC が OAuth の拡張であるという関係上、IdP が Authorization Server を兼ねるケースが多くなりますし、今回だとまさに Keycloak がどちらも兼ねています。

ハンズオン

では、上記のシーケンスを実際に確認していこうと思います。リポジトリの README にもコマンドラインベースでの手順を記載しましたが、ブログではキャプチャをふんだんに入れてみます!

まずは、UserInfo Endpoint を起動します。(コマンドラインかIDEで)

$ export JAVA_HOME=`/usr/libexec/java_home -v 11`
$ java -version
openjdk version "11.0.2" 2019-01-15
$ ./mvnw clean package
$ ./mvnw spring-boot:run

次に、IdP を起動します。

$ docker-compose up -d

を実行したら、.keycloak ディレクトリ以下にある sample-realm.json が読み込まれて、sample realm および realm 内で設定する client, user が作成済みの状態で keyclaok が起動します。

realm とは独立してユーザ、クレデンシャル、ロールなどの設定を管理できる領域です。複数の realm を作った場合、それらは互いに分離されて管理や認証ができます

http://localhost:8000/ にアクセスし、 Administration Console(Master realm) にログインします(Username,Password は dokcer-compose に記載の keycloak)

sample realm に sampel client が作成されているので、 Secret の情報をコピーしておきます。

では、Authorization Endpoint へ 認可コード(Authorization code)を取得するためのリクエストを Postman で作成します。

http://localhost:8000/auth/realms/sample/protocol/openid-connect/auth? をURL入力欄に記載し、Params に Key-Value を追加していきましょう。

  • response_type に code を設定することで、Authorization Server は認可コードの発行を求められていることを知る
  • state はクライアントが生成したランダムな値を設定する
    • ユーザのセッションと紐づけて管理することでクロスサイトフォージェリを防ぐために使用
  • redirect_url は sample realm を登録した際に指定したもの
  • OIDCでは scope に openid の指定が必須

完成したリクエストをコピーして... ブラウザからリクエストを送信すると sample realm のログイン画面が表示されます。

ローカルの検証用なので、Username: sample , Password: 1qazxsw2 でログインできるように設定しています。

ログインに成功すると、sample realm に設定したリダイレクトエンドポイントにリダイレクトされ 認可コードが返されます(フローの4番の箇所)。

code をコピーして次のステップに進みます。

Token Endpoint に対して、認可コードを使い Access Token などを取得するためのリクエストを行います。

Postman で以下のようにリクエストを作成します。

  • grant_type に authorization_code を設定することで、Authorization Code Flow によるリクエストであると分かる
  • code に認可コードを指定
  • client_secret に sample realm の secret をペースト

Postman からリクエストを送信すると、以下のようなトークンレスポンスが返ってきます。(フローの6番の箇所)

アクセストークン、リフレッシュトークン(アクセストークンの再発行に利用する)、有効期限 、と OIDC なので ID Token を取得できました。こちらのアクセストークンをコピーしておきます。


Header に Bearer トークンであるアクセストークンの情報を入れてリクエストしてみましょう!UserInfo Endpoint に認証が通ると200番のHTTPレスポンスステータスコードとリソースが返されます!

Access Token の中身を見てみる

Keycloak はアクセストークンを JWT(JSON Web Token) 形式で提供しています。

jwt.io にて手軽にJWTを Decoded して中身が確認できるようなので、アクセストークンをみてみました。

  • iat(Issued AT) はJWTの発行時間
  • iss(ISSuser) は ID Token の発行者を表します
  • aud(AUDience) は ID Token の発行を受ける Relying_Party のクライアントIDが入ります
  • sub(SUBject) はエンドユーザの識別子
  • scope は OIDC では仕様で規定されており、profile を指定しているため、プロフィール情報へのアクセスが可能です


以上です。実際に動かして挙動を確認することで Authorization Code Flow についてのイメージが少しだけつかめました。

API Gateway pattern と組み合わせて、横断的関心事である認証認可を Spring Cloud Gateway に任せて、下流に Resource Server(UserInfo Endpoint) を複数置いてみる、なども試してみると面白そうです。

参考

https://openid.net/specs/openid-connect-core-1_0.html#Overview

https://openid.net/connect/faq/

https://authya.booth.pm/items/1550861

https://authya.booth.pm/items/1296585

https://www.keycloak.org/docs/latest/getting_started/

https://qiita.com/TakahikoKawasaki/items/498ca08bbfcc341691fe#_reference-9686d2988451466463eb

https://qiita.com/TakahikoKawasaki/items/4ee9b55db9f7ef352b47#1-response_typecode

https://www.udemy.com/share/103Aq4/