Code Review Request for Custom Email-based Authentication in Django REST Framework

Hi everyone,

I am currently learning Django REST Framework (DRF). After working with the built-in token authentication, I decided to experiment by implementing a custom authentication flow using only email (no passwords, just for practice).

I am aware that this is not a secure approach for production environments, but my goal was to understand how custom authentication classes and workflows operate under the hood in DRF.


I would appreciate it if you could review my implementation. I am looking for feedback on:

  1. Code quality and best practices - Are there more idiomatic ways to handle this?

  2. Logical structure - Is the authentication process flow sound?

  3. Security improvements - Even for a practice project, I want to learn the "right" way to handle things.

  4. Alternative approaches -How would you handle this for a specific use case like this?


Here is my current implementation:

models.py

class UserManager(BaseUserManager):
    def create_user(self, email, password, **extra_field):
        if not email:
            raise ValueError("Email field must be set")
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_field)
        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, email, password, **extra_field):
        user = self.create_user(email=email, password=password, **extra_field)
        user.is_superuser = True
        user.is_staff = True
        user.save()
        return user


class User(AbstractBaseUser, PermissionsMixin):
    created_at = models.DateTimeField(auto_now_add=True)
    email = models.EmailField(unique=True, db_index=True)
    is_staff = models.BooleanField(default=False)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []
    objects = UserManager()

serializers.py

class SignupSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ("id", "email", "password")
        extra_kwargs = {
            "password": {"write_only": True, "style": {"input_type": "password"}},
            "id": {"read_only": True}
        }

    def create(self, validated_data):
        return User.objects.create_user(**validated_data)


class UserAuthTokenSerializer(AuthTokenSerializer):
    username = None
    email = serializers.EmailField()
    password = serializers.CharField(
        min_length=8,
        style={"input_type": "password"},
        write_only=True
    )

    def validate(self, attrs):
        email = attrs.get("email")
        password = attrs.get("password")

        if email and password:
            user = authenticate(
                request=self.context.get("request"),
                email=email,
                password=password
            )
            if not user:
                raise serializers.ValidationError(
                    "Unable to log in with provided credentials"
                )
        else:
            raise serializers.ValidationError(
                "Must include username and password."
            )
        attrs["user"] = user
        return attrs

views.py

class SignupAPIView(generics.CreateAPIView):
    serializer_class = SignupSerializer
    permission_classes = (permissions.AllowAny,)
    queryset = User.objects.all()


class TokenAPIView(ObtainAuthToken):
    serializer_class = UserAuthTokenSerializer
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES

urls.py

app_name = "users"

urlpatterns = [
    path("signup/", SignupAPIView.as_view(), name="signup"),
    path("token/", TokenAPIView.as_view(), name="token")
]

I have provided the core logic above. Any tips, criticism, or suggestions for refactoring would be greatly appreciated!

Thanks in advance for your time and help.

Вернуться на верх