├── backend ├── backend │ ├── __init__.py │ ├── asgi.py │ ├── wsgi.py │ ├── urls.py │ └── settings.py ├── user_api │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── admin.py │ ├── apps.py │ ├── urls.py │ ├── serializers.py │ ├── models.py │ ├── validations.py │ └── views.py ├── requirements.txt ├── entrypoint.sh ├── Dockerfile └── manage.py ├── frontend ├── public │ ├── favicon.ico │ ├── robots.txt │ ├── manifest.json │ └── index.html ├── Dockerfile ├── src │ ├── index.css │ ├── App.css │ ├── index.js │ └── App.js └── package.json ├── .env ├── nginx ├── Dockerfile └── default.conf ├── README.md └── docker-compose.yaml /backend/backend/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/user_api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/user_api/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | SECRET_KEY='django-insecure--o+^2-knvxng9b0p=hz8g6-l-y8h15%vn7)v2luv_2#$lq_xef' 2 | DEBUG=True 3 | -------------------------------------------------------------------------------- /backend/user_api/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.19.0-alpine 2 | 3 | COPY ./default.conf /etc/nginx/conf.d/default.conf 4 | -------------------------------------------------------------------------------- /backend/requirements.txt: -------------------------------------------------------------------------------- 1 | Django==4.1.5 2 | django-cors-headers==3.13.0 3 | djangorestframework==3.14.0 4 | gunicorn==20.1.0 -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | 3 | WORKDIR /app 4 | COPY . . 5 | 6 | RUN npm install 7 | 8 | CMD ["npm", "run", "build"] 9 | -------------------------------------------------------------------------------- /backend/user_api/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UserApiConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'user_api' 7 | -------------------------------------------------------------------------------- /backend/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | python manage.py makemigrations 4 | python manage.py migrate --no-input 5 | python manage.py collectstatic --no-input 6 | 7 | gunicorn backend.wsgi:application --bind 0.0.0.0:8000 8 | -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-slim 2 | 3 | RUN pip install --upgrade pip 4 | 5 | COPY ./requirements.txt . 6 | RUN pip install -r requirements.txt 7 | 8 | COPY . /app 9 | WORKDIR /app 10 | 11 | COPY ./entrypoint.sh . 12 | ENTRYPOINT ["sh", "/app/entrypoint.sh"] 13 | -------------------------------------------------------------------------------- /backend/user_api/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | 4 | urlpatterns = [ 5 | path('register', views.UserRegister.as_view(), name='register'), 6 | path('login', views.UserLogin.as_view(), name='login'), 7 | path('logout', views.UserLogout.as_view(), name='logout'), 8 | path('user', views.UserView.as_view(), name='user'), 9 | ] 10 | -------------------------------------------------------------------------------- /nginx/default.conf: -------------------------------------------------------------------------------- 1 | upstream backend { 2 | server backend:8000; 3 | } 4 | 5 | server { 6 | listen 80; 7 | 8 | location /api/ { 9 | proxy_pass http://backend; 10 | } 11 | 12 | location /static/rest_framework/ { 13 | root /static/; 14 | } 15 | 16 | location / { 17 | root /var/www/frontend; 18 | try_files $uri $uri/ /index.html; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React/DjangoRF Authentication App 2 | 3 | Authentication app using React and Django REST framework with session authentication. 4 | 5 | ## Installations 6 | 7 | * backend 8 | ``` 9 | pip install djangorestframework 10 | pip install django-cors-headers 11 | ``` 12 | 13 | * frontend 14 | ``` 15 | npm install axios 16 | npm install react-bootstrap bootstrap 17 | ``` 18 | -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /backend/backend/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for backend project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /backend/backend/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for backend project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | /* boostrap form style */ 11 | 12 | .form-inline { 13 | width: 100%; 14 | } 15 | 16 | .form-group { 17 | width: 90%; 18 | } 19 | 20 | .input-group { 21 | width: 90% !important; 22 | } 23 | 24 | .form-control { 25 | width: 80% !important; 26 | } 27 | 28 | span.input-group-addon { 29 | width: 50px !important; 30 | } 31 | 32 | .center { 33 | display: flex; 34 | justify-content: center; 35 | align-items: center; 36 | margin-top: 5%; 37 | } -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import 'bootstrap/dist/css/bootstrap.min.css'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | //reportWebVitals(); 18 | -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | backend: 5 | volumes: 6 | - static:/static 7 | env_file: 8 | - .env 9 | build: 10 | context: ./backend 11 | ports: 12 | - "8000:8000" 13 | frontend: 14 | build: 15 | context: ./frontend 16 | volumes: 17 | - frontend:/app/build 18 | nginx: 19 | build: 20 | context: ./nginx 21 | volumes: 22 | - static:/static 23 | - frontend:/var/www/frontend 24 | ports: 25 | - "80:80" 26 | depends_on: 27 | - backend 28 | - frontend 29 | 30 | volumes: 31 | static: 32 | frontend: 33 | -------------------------------------------------------------------------------- /backend/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /backend/backend/urls.py: -------------------------------------------------------------------------------- 1 | """backend URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/4.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path, include 18 | 19 | urlpatterns = [ 20 | path('admin/', admin.site.urls), 21 | path('api/', include('user_api.urls')), 22 | ] 23 | -------------------------------------------------------------------------------- /backend/user_api/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from django.contrib.auth import get_user_model, authenticate 3 | 4 | UserModel = get_user_model() 5 | 6 | class UserRegisterSerializer(serializers.ModelSerializer): 7 | class Meta: 8 | model = UserModel 9 | fields = '__all__' 10 | def create(self, clean_data): 11 | user_obj = UserModel.objects.create_user(email=clean_data['email'], password=clean_data['password']) 12 | user_obj.username = clean_data['username'] 13 | user_obj.save() 14 | return user_obj 15 | 16 | class UserLoginSerializer(serializers.Serializer): 17 | email = serializers.EmailField() 18 | password = serializers.CharField() 19 | ## 20 | def check_user(self, clean_data): 21 | user = authenticate(username=clean_data['email'], password=clean_data['password']) 22 | if not user: 23 | raise ValidationError('user not found') 24 | return user 25 | 26 | class UserSerializer(serializers.ModelSerializer): 27 | class Meta: 28 | model = UserModel 29 | fields = ('email', 'username') 30 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "axios": "^1.2.3", 10 | "bootstrap": "^5.2.3", 11 | "http-server": "^14.1.1", 12 | "js-cookie": "^3.0.1", 13 | "react": "^18.2.0", 14 | "react-bootstrap": "^2.7.0", 15 | "react-dom": "^18.2.0", 16 | "react-scripts": "5.0.1", 17 | "web-vitals": "^2.1.4" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject" 24 | }, 25 | "eslintConfig": { 26 | "extends": [ 27 | "react-app", 28 | "react-app/jest" 29 | ] 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /backend/user_api/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.base_user import BaseUserManager 3 | from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin 4 | 5 | class AppUserManager(BaseUserManager): 6 | def create_user(self, email, password=None): 7 | if not email: 8 | raise ValueError('An email is required.') 9 | if not password: 10 | raise ValueError('A password is required.') 11 | email = self.normalize_email(email) 12 | user = self.model(email=email) 13 | user.set_password(password) 14 | user.save() 15 | return user 16 | def create_superuser(self, email, password=None): 17 | if not email: 18 | raise ValueError('An email is required.') 19 | if not password: 20 | raise ValueError('A password is required.') 21 | user = self.create_user(email, password) 22 | user.is_superuser = True 23 | user.save() 24 | return user 25 | 26 | 27 | class AppUser(AbstractBaseUser, PermissionsMixin): 28 | user_id = models.AutoField(primary_key=True) 29 | email = models.EmailField(max_length=50, unique=True) 30 | username = models.CharField(max_length=50) 31 | USERNAME_FIELD = 'email' 32 | REQUIRED_FIELDS = ['username'] 33 | objects = AppUserManager() 34 | def __str__(self): 35 | return self.username 36 | -------------------------------------------------------------------------------- /backend/user_api/validations.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ValidationError 2 | from django.contrib.auth import get_user_model 3 | UserModel = get_user_model() 4 | 5 | def custom_validation(data): 6 | email = data['email'].strip() 7 | username = data['username'].strip() 8 | password = data['password'].strip() 9 | ## 10 | if not email or UserModel.objects.filter(email=email).exists(): 11 | raise ValidationError('choose another email') 12 | ## 13 | if not password or len(password) < 8: 14 | raise ValidationError('choose another password, min 8 characters') 15 | ## 16 | if not username: 17 | raise ValidationError('choose another username') 18 | return data 19 | 20 | 21 | def validate_email(data): 22 | email = data['email'].strip() 23 | if not email: 24 | raise ValidationError('an email is needed') 25 | return True 26 | 27 | def validate_username(data): 28 | username = data['username'].strip() 29 | if not username: 30 | raise ValidationError('choose another username') 31 | return True 32 | 33 | def validate_password(data): 34 | password = data['password'].strip() 35 | if not password: 36 | raise ValidationError('a password is needed') 37 | return True -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /backend/user_api/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model, login, logout 2 | from rest_framework.authentication import SessionAuthentication 3 | from rest_framework.views import APIView 4 | from rest_framework.response import Response 5 | from .serializers import UserRegisterSerializer, UserLoginSerializer, UserSerializer 6 | from rest_framework import permissions, status 7 | from .validations import custom_validation, validate_email, validate_password 8 | 9 | 10 | class UserRegister(APIView): 11 | permission_classes = (permissions.AllowAny,) 12 | def post(self, request): 13 | clean_data = custom_validation(request.data) 14 | serializer = UserRegisterSerializer(data=clean_data) 15 | if serializer.is_valid(raise_exception=True): 16 | user = serializer.create(clean_data) 17 | if user: 18 | return Response(serializer.data, status=status.HTTP_201_CREATED) 19 | return Response(status=status.HTTP_400_BAD_REQUEST) 20 | 21 | 22 | class UserLogin(APIView): 23 | permission_classes = (permissions.AllowAny,) 24 | authentication_classes = (SessionAuthentication,) 25 | ## 26 | def post(self, request): 27 | data = request.data 28 | assert validate_email(data) 29 | assert validate_password(data) 30 | serializer = UserLoginSerializer(data=data) 31 | if serializer.is_valid(raise_exception=True): 32 | user = serializer.check_user(data) 33 | login(request, user) 34 | return Response(serializer.data, status=status.HTTP_200_OK) 35 | 36 | 37 | class UserLogout(APIView): 38 | permission_classes = (permissions.AllowAny,) 39 | authentication_classes = () 40 | def post(self, request): 41 | logout(request) 42 | return Response(status=status.HTTP_200_OK) 43 | 44 | 45 | class UserView(APIView): 46 | permission_classes = (permissions.IsAuthenticated,) 47 | authentication_classes = (SessionAuthentication,) 48 | ## 49 | def get(self, request): 50 | serializer = UserSerializer(request.user) 51 | return Response({'user': serializer.data}, status=status.HTTP_200_OK) 52 | 53 | -------------------------------------------------------------------------------- /backend/backend/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for backend project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.1.4. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.1/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | import os 15 | 16 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 17 | BASE_DIR = Path(__file__).resolve().parent.parent 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = os.getenv('SECRET_KEY') 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = os.getenv('DEBUG') 28 | 29 | ALLOWED_HOSTS = ['*'] 30 | 31 | CORS_ALLOWED_ORIGINS = [ 32 | 'http://localhost', 33 | 'http://127.0.0.1', 34 | 'http://0.0.0.0', 35 | ] 36 | 37 | CORS_ALLOW_CREDENTIALS = True 38 | 39 | 40 | # Application definition 41 | 42 | INSTALLED_APPS = [ 43 | 'django.contrib.admin', 44 | 'django.contrib.auth', 45 | 'django.contrib.contenttypes', 46 | 'django.contrib.sessions', 47 | 'django.contrib.messages', 48 | 'django.contrib.staticfiles', 49 | 'rest_framework', 50 | 'corsheaders', 51 | 'user_api.apps.UserApiConfig', 52 | ] 53 | 54 | MIDDLEWARE = [ 55 | 'corsheaders.middleware.CorsMiddleware', 56 | 'django.middleware.security.SecurityMiddleware', 57 | 'django.contrib.sessions.middleware.SessionMiddleware', 58 | 'django.middleware.common.CommonMiddleware', 59 | 'django.middleware.csrf.CsrfViewMiddleware', 60 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 61 | 'django.contrib.messages.middleware.MessageMiddleware', 62 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 63 | ] 64 | 65 | ROOT_URLCONF = 'backend.urls' 66 | 67 | TEMPLATES = [ 68 | { 69 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 70 | 'DIRS': [], 71 | 'APP_DIRS': True, 72 | 'OPTIONS': { 73 | 'context_processors': [ 74 | 'django.template.context_processors.debug', 75 | 'django.template.context_processors.request', 76 | 'django.contrib.auth.context_processors.auth', 77 | 'django.contrib.messages.context_processors.messages', 78 | ], 79 | }, 80 | }, 81 | ] 82 | 83 | WSGI_APPLICATION = 'backend.wsgi.application' 84 | 85 | 86 | # Database 87 | # https://docs.djangoproject.com/en/4.1/ref/settings/#databases 88 | 89 | DATABASES = { 90 | 'default': { 91 | 'ENGINE': 'django.db.backends.sqlite3', 92 | 'NAME': BASE_DIR / 'db.sqlite3', 93 | } 94 | } 95 | 96 | ## User model 97 | AUTH_USER_MODEL = 'user_api.AppUser' 98 | 99 | REST_FRAMEWORK = { 100 | 'DEFAULT_PERMISSION_CLASSES': ( 101 | 'rest_framework.permissions.IsAuthenticated', 102 | ), 103 | 'DEFAULT_AUTHENTICATION_CLASSES': ( 104 | 'rest_framework.authentication.SessionAuthentication', 105 | ), 106 | } 107 | 108 | # Password validation 109 | # https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators 110 | 111 | AUTH_PASSWORD_VALIDATORS = [ 112 | { 113 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 114 | }, 115 | { 116 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 117 | }, 118 | { 119 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 120 | }, 121 | { 122 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 123 | }, 124 | ] 125 | 126 | 127 | # Internationalization 128 | # https://docs.djangoproject.com/en/4.1/topics/i18n/ 129 | 130 | LANGUAGE_CODE = 'en-us' 131 | 132 | TIME_ZONE = 'UTC' 133 | 134 | USE_I18N = True 135 | 136 | USE_TZ = True 137 | 138 | 139 | # Static files (CSS, JavaScript, Images) 140 | # https://docs.djangoproject.com/en/4.1/howto/static-files/ 141 | 142 | STATIC_URL = 'static/' 143 | STATIC_ROOT = '/static/' 144 | 145 | # Default primary key field type 146 | # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field 147 | 148 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 149 | -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import React from 'react'; 3 | import { useState, useEffect } from 'react'; 4 | import axios from 'axios'; 5 | import Container from 'react-bootstrap/Container'; 6 | import Navbar from 'react-bootstrap/Navbar'; 7 | import Button from 'react-bootstrap/Button'; 8 | import Form from 'react-bootstrap/Form'; 9 | 10 | 11 | axios.defaults.xsrfCookieName = 'csrftoken'; 12 | axios.defaults.xsrfHeaderName = 'X-CSRFToken'; 13 | axios.defaults.withCredentials = true; 14 | 15 | const client = axios.create({ 16 | baseURL: "http://127.0.0.1:8000" 17 | }); 18 | 19 | function App() { 20 | 21 | const [currentUser, setCurrentUser] = useState(); 22 | const [registrationToggle, setRegistrationToggle] = useState(false); 23 | const [email, setEmail] = useState(''); 24 | const [username, setUsername] = useState(''); 25 | const [password, setPassword] = useState(''); 26 | 27 | useEffect(() => { 28 | client.get("/api/user") 29 | .then(function(res) { 30 | setCurrentUser(true); 31 | }) 32 | .catch(function(error) { 33 | setCurrentUser(false); 34 | }); 35 | }, []); 36 | 37 | function update_form_btn() { 38 | if (registrationToggle) { 39 | document.getElementById("form_btn").innerHTML = "Register"; 40 | setRegistrationToggle(false); 41 | } else { 42 | document.getElementById("form_btn").innerHTML = "Log in"; 43 | setRegistrationToggle(true); 44 | } 45 | } 46 | 47 | function submitRegistration(e) { 48 | e.preventDefault(); 49 | client.post( 50 | "/api/register", 51 | { 52 | email: email, 53 | username: username, 54 | password: password 55 | } 56 | ).then(function(res) { 57 | client.post( 58 | "/api/login", 59 | { 60 | email: email, 61 | password: password 62 | } 63 | ).then(function(res) { 64 | setCurrentUser(true); 65 | }); 66 | }); 67 | } 68 | 69 | function submitLogin(e) { 70 | e.preventDefault(); 71 | client.post( 72 | "/api/login", 73 | { 74 | email: email, 75 | password: password 76 | } 77 | ).then(function(res) { 78 | setCurrentUser(true); 79 | }); 80 | } 81 | 82 | function submitLogout(e) { 83 | e.preventDefault(); 84 | client.post( 85 | "/api/logout", 86 | {withCredentials: true} 87 | ).then(function(res) { 88 | setCurrentUser(false); 89 | }); 90 | } 91 | 92 | if (currentUser) { 93 | return ( 94 |
95 | 96 | 97 | Authentication App 98 | 99 | 100 | 101 |
submitLogout(e)}> 102 | 103 |
104 |
105 |
106 |
107 |
108 |
109 |

You're logged in!

110 |
111 |
112 | ); 113 | } 114 | return ( 115 |
116 | 117 | 118 | Authentication App 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | { 128 | registrationToggle ? ( 129 |
130 |
submitRegistration(e)}> 131 | 132 | Email address 133 | setEmail(e.target.value)} /> 134 | 135 | We'll never share your email with anyone else. 136 | 137 | 138 | 139 | Username 140 | setUsername(e.target.value)} /> 141 | 142 | 143 | Password 144 | setPassword(e.target.value)} /> 145 | 146 | 149 |
150 |
151 | ) : ( 152 |
153 |
submitLogin(e)}> 154 | 155 | Email address 156 | setEmail(e.target.value)} /> 157 | 158 | We'll never share your email with anyone else. 159 | 160 | 161 | 162 | Password 163 | setPassword(e.target.value)} /> 164 | 165 | 168 |
169 |
170 | ) 171 | } 172 |
173 | ); 174 | } 175 | 176 | export default App; 177 | --------------------------------------------------------------------------------