Django セッション

keenauth.bff.django は、Django 4.2 以降のアプリケーションにサインイン機能を追加します。サーバーが OAuth クライアントとなり、すべてのトークンをサーバー側で保持します。ブラウザが受け取るのは暗号化された HttpOnly のセッション Cookie のみで、アクセストークン、リフレッシュトークン、ID トークンは一切渡されません。これはバックエンド・フォー・フロントエンド(BFF)パターンです。keenauth-python の一部ですが、まだ PyPI には公開されていません。

ベアラートークンを受け取る API を保護する場合は、代わりに API の保護 を参照してください。

インストール

pip install "keenauth-python[django]"

開始する前に

  1. ダッシュボードで、環境内に Web アプリケーションを作成します。
  2. クライアント認証方式を選択します: private_key_jwt(推奨、PrivateKeyJwt)またはクライアントシークレット(ClientSecretBasic)。
  3. リダイレクト URI https://app.acme.example/auth/callback と、ログアウト後のリダイレクト URI https://app.acme.example/ を追加します。
  4. 32 文字以上の Cookie シークレットを生成します。例: openssl rand -base64 32。リポジトリではなく、シークレットストアに保管してください。

インスタンスを作成する

import os
from keenauth.bff import BffOptions, ClientSecretBasic, KeenAuthBff

bff = KeenAuthBff(BffOptions(
    issuer="https://acme.eu.keenauth.app",  # your environment's issuer, exactly
    client_id="app_01j9zzzzzzzzzzzzzzzzzzzz",
    client_auth=ClientSecretBasic(os.environ["KEENAUTH_CLIENT_SECRET"]),
    app_base_url="https://app.acme.example",
    secret=os.environ["KEENAUTH_COOKIE_SECRET"],
))

app_base_url はアプリの公開アドレスです。SDK はリダイレクト URI とすべてのリダイレクト先をここから組み立てるため、リクエストの Host や X-Forwarded-* ヘッダーからは組み立てません。平文の http は localhost の場合にのみ許可されます。オプションは KeenAuthBff の作成時に検証されるため、設定エラーがあれば起動時にアプリが停止します。

private_key_jwt を使用する

private_key_jwt を使用する場合、アプリケーションに共有シークレットはありません。秘密鍵を PrivateKeyJwt に渡すと、SDK はトークンエンドポイントへのリクエストごとに新しいクライアントアサーション(RFC 7523)に署名します。

from keenauth.bff import PrivateKeyJwt

client_auth = PrivateKeyJwt(key=os.environ["KEENAUTH_PRIVATE_KEY"])  # unencrypted PEM

独自の鍵管理サービス(KMS や HSM)で署名する場合は、代わりに関数を渡します: PrivateKeyJwt(assertion=sign)。sign() は呼び出されるたびに、新しく署名されたアサーションを返す必要があります。

ミドルウェアを追加する

# settings.py
KEENAUTH_BFF = "myproject.auth.bff"  # dotted path to the KeenAuthBff instance
MIDDLEWARE = [
    # ...
    "keenauth.bff.django.KeenAuthMiddleware",
]

ミドルウェアは以下のルートを処理し、それ以外のすべてのリクエストについてセッションを読み取り、request.keenauth に格納します。

# views.py
from django.http import HttpResponse
from keenauth.bff.django import require_auth

@require_auth()
def orders(request):
    return HttpResponse(f"Signed in as {request.keenauth.session.user.email}")

ルート

ルート 処理内容
GET /auth/login サインインを開始します。省略可能なクエリパラメーター: returnTo(ユーザーの遷移先)と organization(組織にサインインします)。
GET または POST /auth/logout KeenAuth でサインアウトし、アプリに戻ります。
GET /auth/callback サインインを完了し、セッション Cookie を設定します。

サインインには PKCE(S256)を用いた認可コードフローを使用します。環境がサポートしている場合は、リクエストをサーバーに事前送信(PAR)します。コールバックでは、SDK が state、iss、および ID トークン(nonce を含む)を検証します。サインインに成功するたびに新しいセッションが作成され、古いセッションは破棄されます。

returnTo にはアプリ自身のパス(例: /orders?tab=open)を指定する必要があります。//evil.example や完全な URL などそれ以外の値は無視され、ユーザーは / に遷移します。

保護されたビュー

ビューには @require_auth() を付けて装飾します。

セッションがない場合:

POST、PUT、PATCH、DELETE など安全でないメソッドの場合、ブラウザはそのリクエストがアプリからのものであることを示す必要があります。具体的には、Sec-Fetch-Site: same-origin または、アプリのオリジンと一致する Origin ヘッダーが必要です。それ以外のリクエストには 403 が返されます。これは SameSite=Lax に加えて、クロスサイトリクエストフォージェリを防止するための仕組みです。

セッションを使用して API を呼び出す

セッションコンテキストには以下が含まれます。

アクセストークンの有効期限が 60 秒以内に迫ると、ビューが実行される前に SDK がトークンを更新します。更新のたびにリフレッシュトークンもローテーションされ、新しいセッション Cookie がレスポンスに設定されます。サーバーがリフレッシュトークンを拒否した場合、セッションは終了します。一時的なサーバーエラーの場合は、セッションは変更されません。1 つのプロセス内で同時に発生したリクエストは、1 回のリフレッシュを共有します。

組織を切り替える

bff.switch_organization_path("org_01j9…", "/dashboard") は、/auth/login へのリンクを返します。このリンクを使うと、ユーザーは指定した組織で再度サインインします。KeenAuth はメンバーシップを確認します。サインイン後は、session.user.org_id に組織が格納されます。

サインアウトする

フォームから /auth/logout に POST を送信します。SDK はセッション Cookie(ストアを使用している場合は保存済みのセッションも)を削除し、リフレッシュトークンを取り消し、ブラウザを環境のログアウトエンドポイントへ送ります。そこからアプリに戻ります。ブラウザが別のサイトからのリクエストとしてマークしたリクエストは拒否されます。Sec-Fetch-Site を送信しないブラウザでは、自社のページからの POST によってのみサインアウトできます。

セッションストレージ

既定では、セッション全体が Cookie に格納されます。Cookie は secret から導出した鍵を用いて AES-256-GCM で暗号化・認証されます。変更またはコピーされた Cookie は拒否されます。Cookie の名前は __Host-keenauth_session.0 で、Secure、HttpOnly、SameSite=Lax、Path=/ を指定して設定されます。http://localhost では __Host- 接頭辞と Secure は使用されません。大きなセッションは最大 3 つの Cookie に分割されます。

トークンをサーバー側で保持するには、get(id)、set(id, session, ttl_seconds)、delete(id) を持つオブジェクトを store= に渡します(例: Redis をバックエンドにしたもの)。この場合、Cookie には暗号化されたランダムなセッション ID のみが格納されます。サインインのたびに新しい ID が作成され、以前のレコードは削除されます。ストアにはトークンが含まれるため、アクセスを制限してください。MemorySessionStore は開発とテストのみを目的としています。

セッションの有効期間は最大 7 日(session_max_age)です。リフレッシュしても有効期間は延長されません。シークレットを変更するには、リストを渡します: secret=[new_secret, old_secret]。古いシークレットは session_max_age の経過後に削除してください。

現在の制限事項