-
学習目的でKeycloakを使ったAuthorization Code Grant + PKCE を検証するための環境を構築したためメモとして残しておく。
-
**過去に作成したコード**を修正して構築する。
- 変更点を主として記述する。
- docker-composeなどの設定ファイルはそのまま利用する。
main.py修正
-
code_verifier・code_challenge生成処理などを追加する。
import ast import urllib.parse as parse import urllib.request as req import urllib.error as error import os import requests import uvicorn from fastapi import FastAPI from starlette.requests import Request from starlette.responses import RedirectResponse import base64 import re import hashlib # 環境変数取得 # FastAPI アプリ用 APP_BASE_URL = os.getenv("APP_BASE_URL") APP_CLIENT_ID = os.getenv("CLIENT_ID") APP_CLIENT_SECRET = os.getenv('CLIENT_SECRET') APP_REDIRECT_URI = os.getenv('REDIRECT_URI') # Keycloak用 # Authorization Endpoint KEYCLOAK_BASE_URL_LOCALHOST = os.getenv("KEYCLOAK_BASE_URL_LOCALHOST") # "Master"は対象のレルム名を指定する。 AUTH_BASE_URL = ( f"{KEYCLOAK_BASE_URL_LOCALHOST}auth/realms/Master" "/protocol/openid-connect/auth" ) # Token Endpoint # コンテナ間通信するためコンテナ名を指定 # "Master"は対象のレルム名を指定する。 KEYCLOAK_BASE_URL_CONTAINER_NAME = os.getenv( "KEYCLOAK_BASE_URL_CONTAINER_NAME") TOKEN_URL = ( f"{KEYCLOAK_BASE_URL_CONTAINER_NAME}auth/realms/master" "/protocol/openid-connect/token" ) app = FastAPI() # Keycloak Authorization Endpointへのリダイレクト @app.get("/auth/login") async def login() -> RedirectResponse: # ステート生成 state = hashlib.sha256(os.urandom(32)).hexdigest() # 追加:code_verifier生成 code_verifier = base64.urlsafe_b64encode(os.urandom(40)).decode('utf-8') code_verifier = re.sub('[^a-zA-Z0-9]+', '', code_verifier) # 追加:code_challenge生成 code_challenge = hashlib.sha256(code_verifier.encode('utf-8')).digest() code_challenge = base64.urlsafe_b64encode(code_challenge).decode('utf-8') code_challenge = code_challenge.replace('=', '') # Authorization Endpointへリダイレクト AUTH_URL = AUTH_BASE_URL + '?{}'.format(parse.urlencode({ 'client_id': APP_CLIENT_ID, 'redirect_uri': APP_REDIRECT_URI, 'state': state, 'response_type': 'code', 'code_challenge':code_challenge, # 追加 'code_challenge_method':'S256' # 追加 })) response = RedirectResponse(AUTH_URL) # ステート、code_verifier保存 ※保存方法要検討。暫定でcookieに保存。 response.set_cookie(key="AUTH_STATE", value=state) response.set_cookie(key="AUTH_CODE_VERIFIER", value=code_verifier) return response # Token Request def get_token(code,code_verifier): params = { 'client_id': APP_CLIENT_ID, 'client_secret': APP_CLIENT_SECRET, 'grant_type': 'authorization_code', 'redirect_uri': APP_REDIRECT_URI, 'code': code, 'code_verifier': code_verifier #追加 } x = requests.post(TOKEN_URL, params, verify=False).content.decode('utf-8') return ast.literal_eval(x) # Redirection Endpoint # ステートと認可コードを受け取る。 # ステート検証後、トークンリクエストを実行する。 @app.get("/auth/callback") async def auth(request: Request, code: str, state: str) -> RedirectResponse: # State検証 if state != request.cookies.get("AUTH_STATE"): return {"error": "state_verification_failed"} # 追加:code_verifier設定 return get_token(code, request.cookies.get("AUTH_CODE_VERIFIER")) if __name__ == "__main__": uvicorn.run(app, port=8000, loop="asyncio")
KeyCloak準備
-
コンテナを起動する
docker-compose up
-
Keycloak Admin コンソールにアクセスする。
http://localhost:8080
※ログイン情報は
docker-compose
に記載 -
クライアント(
Clients
)を登録する。- 「Advanced Settings」->「Proof Key for Code Exchange Code Challenge Method 」で「S256」を選択する。
- その他の登録情報は、**Keycloak: Authorization Code Grant Example**を参考に設定する。
-
テストユーザー(
Users
)を登録する。 -
コンテナを再起動する。
docker-compose down docker-compose build docker-compose up
動作確認
-
Keycloak Authorization Endpointリダイレクト用エンドポイントにアクセスする。
http://localhost:8000/auth/login
-
ユーザー認証を行う。
-
FastAPI側のリダイレクトURIにリダイレクトされ、ブラウザに以下のようなトークンレスポンスJSONが表示される。
{ "access_token": "...", "expires_in": 60, "refresh_expires_in": 1800, "refresh_token": "...", "token_type": "Bearer", "not-before-policy": 0, "session_state": "...", "scope": "profile email" }