feat: create auth app with login, logout, and auth check, including custom backend for email login

This commit is contained in:
Chneemann 2025-03-23 19:54:10 +01:00
parent 5ba7863913
commit 0ac3a5049c
12 changed files with 129 additions and 2 deletions

0
auth_app/__init__.py Normal file
View file

3
auth_app/admin.py Normal file
View file

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
auth_app/apps.py Normal file
View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class AuthAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'auth_app'

12
auth_app/custom_backend.py Executable file
View file

@ -0,0 +1,12 @@
from user_app.models import User
from django.contrib.auth.backends import BaseBackend
class EmailBackend(BaseBackend):
def authenticate(self, request, email=None, password=None):
try:
user = User.objects.get(email=email)
if user.check_password(password):
return user
return None
except User.DoesNotExist:
return None

View file

3
auth_app/models.py Normal file
View file

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

17
auth_app/serializer.py Executable file
View file

@ -0,0 +1,17 @@
from rest_framework import serializers
class LoginSerializer(serializers.Serializer):
email = serializers.EmailField()
password = serializers.CharField(write_only=True)
def validate(self, data):
email = data.get('email')
password = data.get('password')
if not email:
raise serializers.ValidationError("Email is required.")
if not password:
raise serializers.ValidationError("Password is required.")
return data

3
auth_app/tests.py Normal file
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

52
auth_app/views.py Normal file
View file

@ -0,0 +1,52 @@
from django.shortcuts import render, redirect
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status, authentication
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken
from django.contrib.auth import authenticate
from .serializer import LoginSerializer
from rest_framework.permissions import IsAuthenticated
class LoginView(APIView):
serializer_class = LoginSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
email = serializer.validated_data['email']
password = serializer.validated_data['password']
user = authenticate(request, email=email, password=password)
if user:
if not user.is_active:
return Response({'error': 'Account is inactive, please check your mails'}, status=status.HTTP_403_FORBIDDEN)
return self._create_token_response(user)
return Response({'error': 'Unable to login with provided credentials.'}, status=status.HTTP_401_UNAUTHORIZED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def _create_token_response(self, user):
token, created = Token.objects.get_or_create(user=user)
return Response({'token': token.key}, status=status.HTTP_200_OK)
class LogoutView(APIView):
authentication_classes = [authentication.TokenAuthentication]
permission_classes = [IsAuthenticated]
def post(self, request):
try:
request.user.auth_token.delete()
return Response({"message": "Successfully logged out"}, status=status.HTTP_200_OK)
except Exception as e:
return Response({"error": "Logout failed"}, status=status.HTTP_400_BAD_REQUEST)
class AuthView(APIView):
authentication_classes = [authentication.TokenAuthentication]
def get(self, request):
if request.user.is_authenticated:
return Response({"user_id": request.user.id}, status=status.HTTP_200_OK)
return Response({"error": "User is not logged in"}, status=status.HTTP_401_UNAUTHORIZED)

View file

@ -55,10 +55,12 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework.authtoken',
'corsheaders',
'djangorestframework_camel_case',
'task_app',
'user_app',
'auth_app',
]
MIDDLEWARE = [
@ -163,3 +165,21 @@ STATIC_URL = 'static/'
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Auth
AUTH_USER_MODEL = 'user_app.User'
AUTH_EMAIL_VERIFICATION = True
# Rest Framework
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
}
AUTHENTICATION_BACKENDS = (
'auth_app.custom_backend.EmailBackend',
'django.contrib.auth.backends.ModelBackend',
)

View file

@ -3,6 +3,7 @@ from django.urls import path, include
from rest_framework.routers import DefaultRouter
from task_app.views import TaskViewSet, SubTaskViewSet, AssignedTaskViewSet
from user_app.views import UserViewSet
from auth_app.views import LoginView, LogoutView, AuthView
router = DefaultRouter()
router.register(r'tasks', TaskViewSet)
@ -13,4 +14,8 @@ router.register(r'users', UserViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
path('auth/', AuthView.as_view(), name='auth'),
path('auth/login/', LoginView.as_view(), name='login'),
path('auth/logout/', LogoutView.as_view(), name='logout'),
]

View file

@ -1,4 +1,5 @@
from django.contrib import admin
from django import forms
from .models import User
class UserAdmin(admin.ModelAdmin):
@ -9,7 +10,7 @@ class UserAdmin(admin.ModelAdmin):
fieldsets = (
(None, {
'fields': ('first_name', 'last_name', 'email', 'phone', 'password')
'fields': ('first_name', 'last_name', 'email', 'phone', 'password') #Passwort hier beibehalten.
}),
('Additional Info', {
'fields': ('initials', 'color', 'status', 'last_login')
@ -22,4 +23,9 @@ class UserAdmin(admin.ModelAdmin):
list_editable = ('is_active', 'is_staff', 'status')
ordering = ('last_name',)
def save_model(self, request, obj, form, change):
if 'password' in form.changed_data:
obj.set_password(form.cleaned_data['password'])
super().save_model(request, obj, form, change)
admin.site.register(User, UserAdmin)