コンテンツにスキップ

Keycloak@セキュリティ系ミドルウェア

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたする。


01. Keycloakとは

アプリケーションに代わって、認証認可処理を実行する。

認証認可に関するAPIを公開し、認証時のアカウントのCRUDや、認可時のアカウントに対する権限スコープ付与、を実行できる。


01-02. 仕組み

アーキテクチャ

Keycloakは、認証処理サービス、Infinispan、アカウント管理用のRDBMS、といったコンポーネントから構成されている。

keycloak_architecture


Infinispan

キャッシュを保管する。


RDBMS

認証情報を保管する。


02. 認証認可

Realm

Keycloakでは、Adminユーザーの認証はmaster realmで、それ以外はユーザー定義のrealm、で管理する。


認証認可の種類

▼ OIDCの場合

  • 認可コードフロー (標準フロー)
  • 暗黙的フロー


認証情報の伝播方法

▼ クライアントシークレットの場合

記入中...

▼ X509証明書の場合

記入中...

▼ JWTの場合

Keycloakクライアントは、『ヘッダー』『ペイロード』『署名』のそれぞれのJSON型データをbase64方式によってエンコードし、ドットでつなぐ。

これらの処理によって、JWTを作成する。

その後、Keycloakの認可エンドポイントにJWTを送信する。


JWTとクライアントシークレットの場合

記入中...


ユースケース

▼ 認証マイクロサービスとして

記入中...


03. エンドポイント

OIDC

▼ 全体

全ての設定を取得できる。

/realms/<realm名>/.well-known/openid-configuration
$ curl https://<Keycloakのドメイン名>/realms/<realm名>/.well-known/openid-configuration

{
  "issuer": "https://<Keycloakのドメイン名>/realms/<realm名>",
  "authorization_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/auth",
  "token_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/token",
  "introspection_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/token/introspect",
  "userinfo_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/userinfo",
  "end_session_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/logout",
  "frontchannel_logout_session_supported": true,
  "frontchannel_logout_supported": true,
  "jwks_uri": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/certs",
  "check_session_iframe": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/login-status-iframe.html",
  "registration_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/clients-registrations/openid-connect",
  "revocation_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/revoke",
  "device_authorization_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/auth/device",
  "backchannel_authentication_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/ext/ciba/auth",
  "pushed_authorization_request_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/ext/par/request",
  "mtls_endpoint_aliases": {
    "token_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/token",
    "revocation_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/revoke",
    "introspection_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/token/introspect",
    "device_authorization_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/auth/device",
    "registration_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/clients-registrations/openid-connect",
    "userinfo_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/userinfo",
    "pushed_authorization_request_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/ext/par/request",
    "backchannel_authentication_endpoint": "https://<Keycloakのドメイン名>/realms/<realm名>/protocol/openid-connect/ext/ciba/auth"
  },

  ...

}

▼ トークン

フローに応じたトークン (アクセストークン、IDトークン) や認可コードを取得できる。

なお、KeycloakはJWT仕様のアクセストークンを採用している。

/realms/<realm名>/protocol/openid-connect/token

▼ ログアウト

認証を意図的に無効化する。

/realms/<realm名>/protocol/openid-connect/logout

▼ 公開鍵

認証が失効していないか、また不正でないかを検証できる。

/realms/<realm名>/protocol/openid-connect/certs

▼ イントロスペクション

/realms/<realm名>/protocol/openid-connect/token/introspect


04. SLO:シングルログアウト

バックチャネル

▼ IDプロバイダーへのリクエスト

アプリケーションは、IDプロバイダーのログアウトエンドポイント (/logout) にPOSTリクエストを送信する。

# リクエスト
# IDプロバイダーのログアウトエンドポイント
POST /auth/realms/<realm名>/protocol/openid-connect/logout HTTP/1.1
---
Host: <Keycloakのドメイン名>
Content-Type: application/x-www-form-urlencoded
Content-Length: 759
---
client_id=python-client&client_secret=a07f9...8213d1&refresh_token=eyJhbGci...twOA
パラメーター 説明
client_id クライアントID
client_secret クライアントシークレット
refresh_token リフレッシュトークン

▼ アプリケーションへのリクエスト

IDプロバイダーは、バックエンドアプリケーションのバックチャネルログアウトエンドポイント (/k_logout) にPOSTリクエストを送信する。

全てのアプリケーションに対して、この処理を繰り返す。

# リクエスト
# バックエンドアプリケーションのバックチャネルログアウトエンドポイント
POST /auth/k_logout HTTP/1.1
---
Host: localhost:8000
Content-Type: application/x-www-form-urlencoded
Content-Length: 759
---
logout_token=eyJhbGciOiJSUzI1NiIs...zspo4weMQfU-1jL0DxSg

POSTリクエストには、JWT仕様のトークン (たぶんIDトークン) が含まれている。

IDトークンには、アプリケーション間で共有しているクライアントのセッションIDが含まれてする。

Keycloakは、このセッションIDでログアウトすべきクライアントを判定し、認証処理を実行する。

{
  "id": "edfd2bf0-1f2d-4875-a4b1-2752caa07ee1-1606363972255",
  "expiration": 1606364002,
  "resource": "kc-tomcat",
  "action": "LOGOUT",
  # アプリケーション間で共有しているクライアントのセッションID
  "adapterSessionIds": ["FC60BED115518DFB043EDDB77F0E0A8E"],
  "notBefore": 0,
  "keycloakSessionIds": ["ac04ef9d-7793-481c-a5c7-5750560e3c14"],
}

▼ IDプロバイダーからのレスポンス

IDプロバイダーのログアウトエンドポイントは、アプリケーションにレスポンスを送信する。

# レスポンス
HTTP/1.1 204 No Content


フロントチャネル

▼ IDプロバイダーへのリクエスト

ブラウザは、IDプロバイダーのログアウトエンドポイント (/logout) にGETリクエストを送信する。

# リクエスト
# IDプロバイダーのログアウトエンドポイント
GET http://<Keycloakのドメイン名>/auth/realms/<realm名>/protocol/openid-connect/logout?id_token_hint=eyJhbGciOiJS...RE2AZmGgKJAj-HlHw&post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fauth%2Flogout%2Fcomplete&state=e18689b0503aab42574427fb575645aca0065bb758aa8463acf4506fe8a61e81
パラメーター 説明
id_token_hint IDトークン
post_logout_redirect_uri ログアウト後のリダイレクトURL
state CSRF対策の文字列

▼ IDプロバイダーからのレスポンス

IDプロバイダーのログアウトエンドポイントは、ブラウザにレスポンスを送信する。

# レスポンス
HTTP/1.1 307 Temporary Redirect
http://localhost:8000/auth/logout/complete?state=e18689b0503aab42574427fb575645aca0065bb758aa8463acf4506fe8a61e81
パラメーター 説明
state リクエスト時のstateパラメーターの値