Django - Проверка достоверности токена AWS Cognito перед обработкой запроса конечной точки
Так, у меня есть код ниже для проверки токена AWS Cognito. Я явно не хочу добавлять эти 6 строк кода в каждую конечную точку. Также я не знаю, правильный ли это способ проверки, все, что я делаю, это ожидаю, что токен будет иметь формат ' ', разбираю его и просто декодирую часть токена JWT. Как я могу проверить подлинность токена AWS amplify, который приходит с каждым запросом, чтобы убедиться, что пользователь правильно вошел в систему. Могу ли я каким-то образом получить электронную почту пользователя из этого токена?
views.py
def post(self, request):
# 'Bearer z324weroko2iorjqoi=+3r3+3ij.2o2ij4='
token = request.META['HTTP_AUTHORIZATION'].split(' ')[1]
print(token)
# TODO this should be separated out to a login module
try:
res = jwt.decode_cognito_jwt(token)
return Response(status=status.Http_200_OK)
except:
return Response("Invalid JWT", status=status.HTTP_401_UNAUTHORIZED)
Поскольку, похоже, вы используете DRF
, вы можете создать свой собственный класс аутентификации и применить обработку JWT там:
from django.contrib.auth.models import AnonymousUser
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
class MyCustomJWTAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.META['HTTP_AUTHORIZATION'].split(' ')[1]
try:
jwt.decode_cognito_jwt(token)
except Exception:
raise exceptions.AuthenticationFailed('Invalid JWT')
return AnonymousUser(), None
class MyCustomAPIView(APIView):
authentication_classes = (MyCustomJWTAuthentication, )
Или если вы хотите применить его ко всем APIView
s:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'path.to.MyCustomJWTAuthentication',
),
}
Обратите внимание, что если декодирование JWT не удалось, никакие другие классы аутентификации проверяться не будут. Если вы не хотите этого, измените обработку для пункта except
, чтобы не поднимать AuthenticationFailed
.
Если вы используете djangorestframework
, ответ от @bdbd будет лучшим вариантом. В противном случае, возможно, вы захотите рассмотреть следующие варианты:
- Создайте свой собственный декоратор, который будет выполнять аутентификацию. Это имеет ту же идею, что и декоратор @login_required или декоратор @user_passes_test. При написании такого декоратора для представлений, основанных на классах, вас может заинтересовать django.utils.decorators.method_decorator.
from functools import partial, wraps
from django.utils.decorators import method_decorator
def cognito_authenticator(view_func=None):
if view_func is None:
return partial(cognito_authenticator)
@wraps(view_func)
def wrapped_view(request, *args, **kwargs):
# Check the cognito token from the request.
token = request.META['HTTP_AUTHORIZATION'].split(' ')[1]
try:
res = jwt.decode_cognito_jwt(token)
# Authenticate res if valid. Raise exception if not.
except Exception:
# Fail if invalid
return HttpResponseForbidden("You are forbidden here!")
else:
# Proceed with the view if valid
return view_func(request, *args, **kwargs)
return wrapped_view
# We can decorate it here before the class definition but can also be done before the class method itself. See https://docs.djangoproject.com/en/3.2/topics/class-based-views/intro/#decorating-the-class
@method_decorator(
name="post",
decorator=[
cognito_authenticator,
],
)
class SomeView(View):
@method_decorator(cognito_authenticator) # As explained above, this is another way of putting the decorator
def get(self, request):
return HttpResponse("Allowed entry!")
def post(self, request):
return HttpResponse("Allowed entry!")
# Or if using function-based views
@api_view(['POST'])
@cognito_authenticator
def some_view(request):
return HttpResponse(f"Allowed entry!")
- Напишите пользовательское промежуточное ПО. Учтите, что порядок порядка имеет значение. Та же идея, что и у стандартного AuthenticationMiddleware, которое заполняет поле
request.user
. В вашем случае реализуйте метод__call__
, в котором вы будете проверять токены Cognito. Не переходите к представлению, если токен недействителен, возвращая, например,HttpResponseForbidden
, как в этом reference. .
class CognitoAuthenticatorMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
token = request.META['HTTP_AUTHORIZATION'].split(' ')[1]
try:
res = jwt.decode_cognito_jwt(token)
# Authenticate res if valid. Raise exception if not.
except Exception:
# Fail if invalid
return HttpResponseForbidden("You are forbidden here!")
# Proceed if valid
response = self.get_response(request)
return response
MIDDLEWARE = [
...
'path.to.CognitoAuthenticatorMiddleware',
...
]
Update
Здесь приведен пример запуска с использованием варианта 1. Для простоты settings.py
- это настройки по умолчанию.
views.py
from functools import partial, wraps
from django.http import HttpResponse, HttpResponseForbidden
from django.utils.decorators import method_decorator
from django.views import View # If using django views
from rest_framework.views import APIView # If using djangorestframework views
def cognito_authenticator(view_func=None):
if view_func is None:
return partial(cognito_authenticator)
@wraps(view_func)
def wrapped_view(request, *args, **kwargs):
# To simplify the authentication, we would check if there is a query parameter "name=me". If none, it is forbidden.
if request.GET.get('name') == "me":
return view_func(request, *args, **kwargs)
return HttpResponseForbidden("You are forbidden here!")
return wrapped_view
@method_decorator( # Try this style-1
name="get",
decorator=[
cognito_authenticator,
],
)
class SomeView(View): # If using djangorestframework view, this can also inherit from APIView or others e.g. class SomeView(APIView):
@method_decorator(cognito_authenticator) # Or try this style-2
def get(self, request):
return HttpResponse(f"Allowed entry!")
urls.py
from django.urls import path
from my_app import views
urlpatterns = [
path("some-view/", views.SomeView.as_view()),
]
Прогон образца:
$ curl http://127.0.0.1:8000/my_app/some-view/?name=notme
You are forbidden here!
$ curl http://127.0.0.1:8000/my_app/some-view/?name=me
Allowed entry!