- 学習目的でKeycloakを使ったAuthorization Code Grantを検証するための環境を構築したので、メモとして残しておく。
- アクセストークンを取得するところまで。
構成
- Dockerを用いた以下の構成で検証環境を構築する。
keycloak ___ docker-compose.yml
|__ app.env
|__ app ___ Dockerfile
|__ requirements.txt
|__ api _ main.py
docker-compose.yml
- FastAPI - Keycloak - MySQLコンテナ定義
version: "3"
volumes:
mysql_data:
driver: local
services:
mysql:
image: mysql:5.7
volumes:
- mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: keycloak
MYSQL_USER: keycloak
MYSQL_PASSWORD: password
keycloak:
image: quay.io/keycloak/keycloak:latest
container_name: "keycloak"
environment:
DB_VENDOR: MYSQL
DB_ADDR: mysql
DB_DATABASE: keycloak
DB_USER: keycloak
DB_PASSWORD: password
KEYCLOAK_USER: admin
KEYCLOAK_PASSWORD: P@ssw0rd
ports:
- 8080:8080
depends_on:
- mysql
app:
container_name: "app"
env_file: app.env
build: ./app
volumes:
- ./app/api:/usr/src/server
ports:
- "8000:8000"
app.env
- 環境変数設定
- 一度Keycloakコンテナを起動して、登録したクライアント情報を設定する。
APP_BASE_URL=http://localhost:8000/
KEYCLOAK_BASE_URL_LOCALHOST=http://localhost:8080/
KEYCLOAK_BASE_URL_CONTAINER_NAME=http://keycloak:8080/
CLIENT_ID=YOUR_CLIENT_ID
CLIENT_SECRET=YOUR_CLIENT_SECRET
REDIRECT_URI=http://localhost:8000/auth/callback
requirements.txt
- Python依存ライブラリ
fastapi
requests
uvicorn
Dockerfile
- 依存ライブラリインストールとFastAPI起動オプションを指定。
FROM python:3.7
WORKDIR /usr/src/server
ADD requirements.txt .
RUN pip install -r requirements.txt
# uvicornのオプションに--reloadを付与し、
# main.pyの編集と同時に変更内容を反映させる。
CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8000"]
main.py
- 以下を行う。
- Keycloak Authorization Endpointへのリダイレクト(
login
) - Keycloak Authorization Responseの受信+トークンリクエスト(
auth
)
- Keycloak Authorization Endpointへのリダイレクト(
import ast
import urllib.parse as parse
import os
import requests
import uvicorn
from fastapi import FastAPI
from starlette.requests import Request
from starlette.responses import RedirectResponse
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
# コンテナ間通信するため、localhostではなくコンテナ名を指定
# "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()
# 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'
}))
response = RedirectResponse(AUTH_URL)
# ステート保存
response.set_cookie(key="AUTH_STATE", value=state)
return response
# Token Request
def get_token(code):
params = {
'client_id': APP_CLIENT_ID,
'client_secret': APP_CLIENT_SECRET,
'grant_type': 'authorization_code',
'redirect_uri': APP_REDIRECT_URI,
'code': code
}
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"}
return get_token(code)
if __name__ == "__main__":
uvicorn.run(app, port=8000, loop="asyncio")
動作確認
-
コンテナを起動する
docker-compose up
-
Keycloak Admin コンソールにアクセスする。
http://localhost:8080
※ログイン情報は
docker-compose
に記載 -
クライアント(
Clients
)を登録する。
※各種登録情報は、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" }