LoginSignup
2
0

More than 1 year has passed since last update.

Keycloakを用いたAuthorization Code Grant + PKCE 検証環境構築方法 メモ

Last updated at Posted at 2022-01-21
  • 学習目的で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"
  }

参考情報

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0