パッケージ@Flask¶
はじめに¶
本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。
01. authlib.integrations.flask_client¶
authlib.integrations.flask_clientとは¶
初期化¶
▼ 全体¶
特にKubernetes内では、api_base_url
とauthorize_url
のドメインに、外部から接続できるKeycloakのドメインを設定する。
一方で、access_token_url
とjwks_uri
はKubernetes内部からのみ接続できるように、Serviceのドメインとする。
from authlib.integrations.flask_client import OAuth
app = Flask(__name__)
oauth = OAuth(app)
oauth.register(
# 例:keycloak、googleなど
name="<IDプロバイダー名>",
client_id="foo-client",
client_secret="*****",
client_kwargs={"scope": "openid profile email"},
# 外部から接続できるKeycloakのドメインを設定する。Kubernetes Serviceのドメインではダメ。
api_base_url="http://<KeycloakのAPIのドメイン (Serviceの完全修飾ドメイン名) >/",
# 外部から接続できるKeycloakのドメインを設定する。Kubernetes Serviceのドメインではダメ。
authorize_url="http://<KeycloakのWebのドメイン>/realms/<realm名>>/protocol/openid-connect/auth",
# Serviceの完全修飾ドメイン名
access_token_url="http://<KeycloakのAPIのドメイン>:8080/realms/dev/protocol/openid-connect/token",
# Serviceの完全修飾ドメイン名
jwks_uri="http://<KeycloakのAPIのドメイン (Serviceの完全修飾ドメイン名) >:8080/realms/dev/protocol/openid-connect/certs"
)
IDプロバイダー名を指定し、ここで設定した変数を取得できる。
# http://<KeycloakのAPIのドメイン>/
print(oauth.<IDプロバイダー名>.api_base_url)
- https://docs.authlib.org/en/latest/client/flask.html
- https://docs.authlib.org/en/latest/client/flask.html#flask-openid-connect-client
- https://github.com/authlib/demo-oauth-client/blob/master/flask-google-login/app.py
- https://github.com/hiroki-it/istio/blob/master/samples/bookinfo/src/productpage/productpage.py
ログイン¶
▼ アクセストークンをAuthorization
ヘッダーで運搬する場合¶
フロントエンドアプリケーションがCSRの場合に採用できる。
PythonでCSRのアプリケーションを実現できないため、Authorization
ヘッダーの運搬は実装できない。
▼ アクセストークンをCookie
ヘッダーで運搬する場合¶
フロントエンドアプリケーションがCSRまたはSSRの場合に採用できる。
CSRまたはSSRのアプリケーションは、Cookie
ヘッダーを介してブラウザのCookieにトークンを保存できる。
from flask import url_for, redirect
@app.route('/login')
def login():
redirect_uri = url_for("callback", _external=True)
return oauth.keycloak.authorize_redirect(redirect_uri)
@app.route("/callback")
def callback():
response = app.make_response(redirect(url_for('front', _external=True)))
try:
# 各種トークンを取得する
token = oauth.keycloak.authorize_access_token()
session['id_token'] = token['id_token']
# デコードしたIDトークンを取得する
id_token = oauth.keycloak.parse_id_token(token, None)
session['user'] = id_token['given_name']
# access_tokenというキー名でCookieにアクセストークンを設定する
# レスポンスにSet-Cookieヘッダーが追加される
response.set_cookie('access_token', token['access_token'])
except BaseException:
logging.info("failed to callback")
return response
ログアウト¶
▼ アクセストークンをAuthorization
ヘッダーで運搬する場合¶
フロントエンドアプリケーションがCSRの場合に採用できる。
PythonでCSRのアプリケーションを実現できないため、Authorization
ヘッダーの運搬は実装できない。
▼ アクセストークンをCookie
ヘッダーで運搬する場合¶
from flask import url_for, redirect
@app.route('/logout')
def logout():
# Keycloakからログアウトし、ホームにリダイレクトする
redirect_uri = ("http://localhost:8080/realms/dev/protocol/openid-connect/logout?id_token_hint=%s&post_logout_redirect_uri=%s" % (session.get('id_token', ''), url_for("home", _external=True)))
session.pop('id_token', None)
session.pop('user', None)
response = app.make_response(redirect(redirect_uri))
# Cookieヘッダーのアクセストークンを削除する
response.delete_cookie('access_token')
return response
認証情報の失効¶
▼ アクセストークンをAuthorization
ヘッダーで運搬する場合¶
フロントエンドアプリケーションがCSRの場合に採用できる。
PythonでCSRのアプリケーションを実現できないため、Authorization
ヘッダーの運搬は実装できない。
▼ アクセストークンをCookie
ヘッダーで運搬する場合¶
from flask import url_for, redirect
@app.route('/')
def home():
product_id = 0
headers = getForwardHeaders(request)
user = session.get('user', '')
product = getProduct(product_id)
# detailsサービスにリクエストを送信する
detailsStatus, details = getProductDetails(product_id, headers)
logging.info("[" + str(detailsStatus) + "] details response is " + str(details))
# reviewsサービスにリクエストを送信する
reviewsStatus, reviews = getProductReviews(product_id, headers)
logging.info("[" + str(reviewsStatus) + "] reviews response is " + str(reviews))
# いずれかのマイクロサービスでアクセストークンの検証が失敗し、401ステータスが返信された場合、ログアウトする
if detailsStatus == 401 or reviewsStatus == 401:
logging.info("[" + str(401) + "] session has expired.")
redirect_uri = url_for('logout', _external=True)
return redirect(redirect_uri)
response = app.make_response(render_template(
'productpage.html',
detailsStatus=detailsStatus,
reviewsStatus=reviewsStatus,
product=product,
details=details,
reviews=reviews,
user=user))
return response
02. flask_oidc.OpenIDConnect¶
flask_oidc.OpenIDConnectとは¶
from flask_oidc import OpenIDConnect
app = Flask(__name__)
app.config.update({
# client_secrets.jsonファイルのパスを設定する
'OIDC_CLIENT_SECRETS': 'client_secrets.json',
'OIDC_SCOPES': ['openid', 'profile', 'email'],
})
oidc = OpenIDConnect(app)
client_secrets.json¶
認証情報をJSONファイルで定義する。
{
"web":
{
"issuer": "http://localhost:8081/realms/<realm名>",
"auth_uri": "http://localhost:8081/realms/<realm名>/protocol/openid-connect/auth",
"client_id": "flask-app",
"client_secret": "a41060dd-b5a8-472e-a91f-6a3ab0e04714",
"redirect_uris": ["http://localhost:5000/*"],
"userinfo_uri": "http://localhost:8081/realms/<realm名>/protocol/openid-connect/userinfo",
"token_uri": "http://localhost:8081/realms/<realm名>/protocol/openid-connect/token",
"token_introspection_uri": "http://localhost:8081/realms/<realm名>/protocol/openid-connect/token/introspect",
},
}