以下の内容はhttps://kamatimaru.hatenablog.com/entry/2024/12/31/114638より取得しました。


勉強のためにOpen ID ConnectのIDプロバイダー側をDjangoで実装する⑥

以下の続き kamatimaru.hatenablog.com

トークンエンドポイント

空のエンドポイントの実装

まずは空のエンドポイントを実装していく。

Viewを定義する。トークンエンドポイントはPOSTのみ。

views.py

class TokenView(View):
    def post(self, request):
        return JsonResponse({})

urlを追加する。

urls.py

urlpatterns = [
    # ...省略
    path("token/", views.TokenView.as_view(), name="token"),
]

以下のリクエストパラメータを定義したFormを実装する。grant_typeauthorization_code以外を許可しないバリデーションも実装する。

パラメータ名 説明
redirect_uri 認可リクエスト時と同じredirect_uriかの検証に使う
grant_type 認可コードフローの場合は固定でauthorization_codeという文字列を渡す
code 認可コードフローの場合は固定でauthorization_codeという文字列を渡す
client_id RPのclient_id
client_secret RPのclient_secret

forms.py

class TokenForm(forms.Form):
    redirect_uri = forms.CharField()
    grant_type = forms.CharField()
    code = forms.CharField()
    client_id = forms.CharField()
    client_secret = forms.CharField()

    def clean_grant_type(self):
        grant_type = self.cleaned_data["grant_type"]
        if grant_type != "authorization_code":
            raise forms.ValidationError("Invalid grant_type")
        return grant_type

第一段階としてTokenViewでパラメータをダンプしてみる。

class TokenView(View):
    def post(self, request):
        form = TokenForm(request.POST)
        if not form.is_valid():
            return HttpResponseBadRequest()
        print(form.cleaned_data)
        return JsonResponse({})

以下のcurlを実行する

curl -X POST "http://localhost:8000/sample/token/" -d "redirect_uri=http://localhost/callback" -d "grant_type=authorization_code" -d "code={code}" -d "client_id=test" -d "client_secret=2ft5H5U3WiusDp9a"

CSRFで弾かれた。

<!-- ...省略 -->
 <p>You are seeing this message because this site requires a CSRF cookie when submitting forms. This cookie is required for security reasons, to ensure that your browser is not being hijacked by third parties.</p>
  <p>If you have configured your browser to disable cookies, please re-enable them, at least for this site, or for “same-origin” requests.</p>
<!-- ...省略 -->

OIDCの場合はCSRFは別の方法で対策するので、ViewにCSRFを除外する宣言を追加する。 class-based viewsにデコレータをつける方法は以下

https://docs.djangoproject.com/ja/4.2/topics/class-based-views/intro/#id1

views.py

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
# ...省略

@method_decorator(csrf_exempt, name="dispatch")
class TokenView(View):
    # ...省略

もう1回実行すると以下のようにパラメータをダンプできた。

{'redirect_uri': 'http://localhost/callback', 'grant_type': 'authorization_code', 'code': '{code}', 'client_id': 'test', 'client_secret': '2ft5H5U3WiusDp9a'}
[31/Dec/2024 02:11:23] "POST /sample/token/ HTTP/1.1" 200 2

バリデーションの実装

リクエストパラメータのバリデーションを実装していく。 まずはクライアント(=RP)が存在するかつシークレットが正しいかを検証する。

views.py

class TokenView(View):
    def post(self, request):
        # ...省略
        try:
          relying_party = RelyingParty.objects.get(
              client_id=form.cleaned_data["client_id"]
          )
        except RelyingParty.DoesNotExist:
            return HttpResponseBadRequest()
        if not check_password(
            form.cleaned_data["client_secret"], relying_party.client_secret
        ):
            return HttpResponseBadRequest()
        return JsonResponse({})

client_secretが正しい時は200で間違ってる時は400が返ってくるのでok

[31/Dec/2024 02:27:31] "POST /sample/token/ HTTP/1.1" 200 2
Bad Request: /sample/token/
[31/Dec/2024 02:29:08] "POST /sample/token/ HTTP/1.1" 400 0

認可コードが有効かを検証する。

class TokenView(View):
    def post(self, request):
        # ...省略
        try:
            authorization_code = AuthorizationCode.objects.get(
                code=form.cleaned_data["code"],
                client=relying_party,
                expired_at__gte=datetime.now(),
            )
        except AuthorizationCode.DoesNotExist:
            return HttpResponseBadRequest()
        return JsonResponse({})

最後にredirect_uriが認可リクエスト時と同じかを検証する。認可リクエスト時のredirect_uriAuthorizationCodeから取得できる。

views.py

class TokenView(View):
    def post(self, request):
        # ...省略
        if form.cleaned_data["redirect_uri"] != authorization_code.redirect_uri:
            return HttpResponseBadRequest()
        return JsonResponse({})

ここまででリクエストパラメータのバリデーションは実装できたので、次はトークンの発行処理を実装していく。

今日はここまで。




以上の内容はhttps://kamatimaru.hatenablog.com/entry/2024/12/31/114638より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14