from allauth.account.views import ConfirmEmailView
from django.conf import settings
from django.http import HttpResponseNotAllowed, HttpResponseRedirect
from django.urls import reverse
from rest_framework.permissions import AllowAny
from rest_framework.views import APIView
from rest_framework_simplejwt.exceptions import InvalidToken
from jwt_allauth.constants import (
EMAIL_VERIFIED_REDIRECT,
PASSWORD_SET_REDIRECT,
FOR_USER,
ONE_TIME_PERMISSION,
PASS_SET_ACCESS,
SET_PASSWORD_COOKIE,
EMAIL_CONFIRMATION,
)
from jwt_allauth.registration.email_verification.serializers import VerifyEmailSerializer
from jwt_allauth.tokens.app_settings import RefreshToken
from jwt_allauth.tokens.models import GenericTokenModel, RefreshTokenWhitelistModel
from jwt_allauth.tokens.serializers import GenericTokenModelSerializer
[docs]
class VerifyEmailView(APIView, ConfirmEmailView):
permission_classes = (AllowAny,)
allowed_methods = ('GET',)
# URL where the frontend password-set flow is implemented (admin-managed registration)
# By default, point to the built-in HTML UI provided by this library.
form_url = getattr(settings, PASSWORD_SET_REDIRECT, '/registration/set-password/default/')
[docs]
@staticmethod
def get_serializer(*args, **kwargs):
return VerifyEmailSerializer(*args, **kwargs)
[docs]
def get(self, request, *args, **kwargs):
# If admin-managed registration is enabled, validate the confirmation key and
# issue a one-time token to allow the user to set their password.
if getattr(settings, 'JWT_ALLAUTH_ADMIN_MANAGED_REGISTRATION', False):
# Ensure PASSWORD_SET_REDIRECT has been configured
if self.form_url is None:
raise NotImplementedError('`PASSWORD_SET_REDIRECT` must be configured in settings.py')
# Check that the email confirmation token has not been used already
query_set = GenericTokenModel.objects.filter(
token=kwargs['key'],
purpose=EMAIL_CONFIRMATION,
)
if len(query_set) != 1:
raise InvalidToken()
# Single-use confirmation token
query_set.delete()
confirmation = self.get_object()
user = confirmation.email_address.user
# Confirm the email now
confirmation.confirm(self.request)
# Create one-time access token to allow setting the password
refresh_token = RefreshToken()
refresh_token[FOR_USER] = user.id
refresh_token[ONE_TIME_PERMISSION] = PASS_SET_ACCESS
access_token = refresh_token.access_token
response = HttpResponseRedirect(self.form_url)
response.set_cookie(
key=SET_PASSWORD_COOKIE,
value=str(access_token),
httponly=getattr(settings, 'PASSWORD_SET_COOKIE_HTTP_ONLY', True),
secure=getattr(settings, 'PASSWORD_SET_COOKIE_SECURE', not settings.DEBUG),
samesite=getattr(settings, 'PASSWORD_SET_COOKIE_SAME_SITE', 'Lax'),
max_age=getattr(settings, 'PASSWORD_SET_COOKIE_MAX_AGE', 3600 * 24),
)
token_serializer = GenericTokenModelSerializer(
data={
'token': access_token['jti'],
'user': user.id,
'purpose': PASS_SET_ACCESS,
}
)
token_serializer.is_valid(raise_exception=True)
token_serializer.save()
return response
# Default flow: just confirm the email and enable refresh tokens
confirmation = self.get_object()
# Enable refresh token
refresh = RefreshTokenWhitelistModel.objects.filter(user=confirmation.email_address.user).first()
if refresh:
refresh.enabled = True
refresh.save()
confirmation.confirm(self.request)
return HttpResponseRedirect(
getattr(settings, EMAIL_VERIFIED_REDIRECT, reverse('jwt_allauth_email_verified'))
)
[docs]
def post(self, request, *args, **kwargs):
return HttpResponseNotAllowed(['GET'])