djoser is a library that supports basic user authentication and registration on the Django REST Framework. It can also be used for custom models, and is designed for an architecture that fits better with a Single Page Application (SPA) rather than reusing Django's code.
This time I will write about the implementation of the simplest authentication function of djoser. Please note that this authentication should not be actually used for security reasons, and there are stronger security settings such as the JWT authentication below. I will introduce it as a simple authentication to the last.
The source code is here
All of the following can be used as endpoints after installation.
/users/ /users/me/ /users/confirm/ /users/resend_activation/ /users/set_password/ /users/reset_password/ /users/reset_password_confirm/ /users/set_username/ /users/reset_username/ /users/reset_username_confirm/ /token/login/ (Token Based Authentication) /token/logout/ (Token Based Authentication) /jwt/create/ (JSON Web Token Authentication) /jwt/refresh/ (JSON Web Token Authentication) /jwt/verify/ (JSON Web Token Authentication) Getting started
I have written other articles related to djoser.
-Implementation of authentication function in Django REST Framework using djoser -Implementation of JWT authentication function in Django REST Framework using djoser
This time, we will implement user authentication using JWT authentication.
The flow up to the middle is the same as the JWT authentication time, and the major changes are writing models.py by yourself and increasing some settings.py.
$ pip install -U djoser
Since JWT authentication is used, you need to use simple_jwt as well.
$ pip install -U djangorestframework_simplejwt
First, make a project,
$ django-admin startproject djoser_customuser
Go inside the project and
$ cd djoser_customuser
Create an app.
$ django-admin startapp accounts
Next, let's set up Django.
setings.py
from datetime import timedelta # add
.........
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', # add
'djoser', # add
'accounts' # add
]
# add
SIMPLE_JWT = {
#Set token to JWT
'AUTH_HEADER_TYPES':('JWT'),
#Token duration setting
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60)
}
# add
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
}
# add
AUTH_USER_MODEL = 'user.accounts'
urls.py
from django.contrib import admin
from django.urls import path,include #add
urlpatterns = [
path('admin/', admin.site.urls),
path('api/auth/',include('djoser.urls')), #add
path('api/auth/',include('djoser.urls.jwt')), #add
]
From here, I will write a custom user model for the part that is significantly different from the JWT authentication article. Regarding why we use a custom user model instead of the default one, in general, the default user model is not enough to create the web application you want, and development has progressed. Since it is difficult to modify the user model later, it is desirable to design a custom user model that meets the requirements in advance.
In addition, Django comes with two classes, the "AbstractUser" class and the "AbstractBaseUser", which come by default.
Since it is similar to the User class, it is easy to use because it requires less coding. Therefore, it is often used when only adding attributes.
It requires more coding because you have to write the user name and other attributes, but it is more flexible than AbstractUser.
Generally, it has the above characteristics. This time, we will use "AbstactBaseUser".
models.py
from django.db import models
# add
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.utils import timezone
from django.core.mail import send_mail
from django.contrib.auth.base_user import BaseUserManager
# add
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, username, email, password, **extra_fields):
"""
Create and save a user with the given username, email, and password.
"""
if not username:
raise ValueError('The given username must be set')
email = self.normalize_email(email)
username = self.model.normalize_username(username)
user = self.model(username=username, email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, username, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(username, email, password, **extra_fields)
def create_superuser(self, username, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(username, email, password, **extra_fields)
class User(AbstractBaseUser, PermissionsMixin):
"""
An abstract base class implementing a fully featured User model with
admin-compliant permissions.
Username and password are required. Other fields are optional.
"""
username_validator = UnicodeUsernameValidator()
username = models.CharField(
_('username'),
max_length=150,
unique=True,
help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
validators=[username_validator],
error_messages={
'unique': _("A user with that username already exists."),
},
)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=150, blank=True)
email = models.EmailField(_('email address'), blank=True,unique=True)
profile = models.CharField(_('profile'), max_length=255, blank=True) # add
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this admin site.'),
)
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this user should be treated as active. '
'Unselect this instead of deleting accounts.'
),
)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
objects = UserManager()
EMAIL_FIELD = 'email' # fix
USERNAME_FIELD = 'email' # fix
REQUIRED_FIELDS = ['username'] # fix
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def clean(self):
super().clean()
self.email = self.__class__.objects.normalize_email(self.email)
def get_full_name(self):
"""
Return the first_name plus the last_name, with a space in between.
"""
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
"""Return the short name for the user."""
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs):
"""Send an email to this user."""
send_mail(subject, message, from_email, [self.email], **kwargs)
After that, the flow is exactly the same as the JWT article. However, there is a difference, first in models.py
models.py
EMAIL_FIELD = 'email' # fix
USERNAME_FIELD = 'email' # fix
REQUIRED_FIELDS = ['username'] # fix
Until now, the user name was a required item when creating a user, but this time Email is a required item. When actually using some kind of Web service, email should be a required item rather than username. These are also the advantages of using custom users.
Migrate and create superuser.
$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py createsuperuser
Email address: [email protected]
Username: Admin
Password:***********
Start on localhost and specify http://localhost:8000/api/auth To access.
$ python manage.py runserver
And http://localhost:8000/api/auth/users/ When you access
User information cannot be obtained because it is still not authenticated. So, like last time, to get a token http://localhost:8000/api/auth/jwt/create/ Access, enter the value of the superuser you registered earlier, and press POST at the bottom right.
Then, the following token will be issued, and the token of access at the bottom will be copied.
Let's actually get the user information using the curl command.
curl -LX GET http://127.0.0.1:8000/api/auth/users/me/ -H 'Authorization: JWT xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
(Enter the token in xxxx)
Then
{"username":"Admin","id":1,"email":"[email protected]"}
The user information you registered earlier will be returned.
Recommended Posts