User-related REST API based on the awesome Django REST Framework

Overview

Django REST Registration

CircleCI Build Status Codecov Coverage PyPi Version Documentation Status

User registration REST API, based on Django REST Framework.

Documentation

Full documentation for the project is available at https://django-rest-registration.readthedocs.io/.

Requirements

  • Django (1.10+, 2.0+, 3.0+) and Django-REST-Framework (3.3+)
  • Python 3.4 or higher (no Python 2 support!)

Features

  • Supported views:
    • registration (sign-up) with verification
    • login/logout (sign-in), session- or token-based
    • user profile (retrieving / updating)
    • reset password
    • change password
    • register (change) e-mail
  • Views are compatible with django-rest-swagger
  • Views can be authenticated via session or auth token
  • Modeless (uses the user defined by settings.AUTH_USER_MODEL and also uses cryptographic signing instead of profile models)
  • Uses password validation
  • Heavily tested (Above 98% code coverage)

Current limitations

Installation & Configuration

You can install Django REST Registration latest version via pip:

pip install django-rest-registration

Then, you should add it to the INSTALLED_APPS so the app templates for notification emails can be accessed:

INSTALLED_APPS=(
    ...

    'rest_registration',
)

After that, you can use the urls in your urlconfig, for instance (using new Django 2.x syntax):

api_urlpatterns = [
    ...

    path('accounts/', include('rest_registration.api.urls')),
]


urlpatterns = [
    ...

    path('api/v1/', include(api_urlpatterns)),
]

In Django 1.x you can use old url instead of path.

You can configure Django REST Registraton using the REST_REGISTRATION setting in your Django settings (similarly to Django REST Framework).

Below is sample, minimal config you can provide in your django settings which will satisfy the system checks:

REST_REGISTRATION = {
    'REGISTER_VERIFICATION_ENABLED': False,
    'RESET_PASSWORD_VERIFICATION_ENABLED': False,
    'REGISTER_EMAIL_VERIFICATION_ENABLED': False,
}

However, the preferred base configuration would be:

REST_REGISTRATION = {
    'REGISTER_VERIFICATION_URL': 'https://frontend-host/verify-user/',
    'RESET_PASSWORD_VERIFICATION_URL': 'https://frontend-host/reset-password/',
    'REGISTER_EMAIL_VERIFICATION_URL': 'https://frontend-host/verify-email/',

    'VERIFICATION_FROM_EMAIL': '[email protected]',
}

The frontend urls are not provided by the library but should be provided by the user of the library, because Django REST Registration is frontend-agnostic. The frontend urls will receive parameters as GET query and should pass them to corresponding REST API views via HTTP POST request.

In case when any verification is enabled (which is the default!), your Django application needs to be properly configured so it can send e-mails.

You can read more about basic configuration here.

You can read more about detailed configuration here.

Configuration options

You can find all REST_REGISTRATION configuration options here.

Contributing

If you want to contribute, please refer to separate document CONTRIBUTING.md.

Comments
  • Token authentication with expiring token.

    Token authentication with expiring token.

    It would be a nice feature if rest-registration provides inbuilt token authentication with expiring token. JWT provides expiring tokens but handling logout with blacklisting token is a headache that I feel but in the case of expiring token, it doesn't need any blacklisting on logout. It will increase the strength of authentication with the token mechanism.

    type:feature-request 
    opened by itzmanish 11
  • Swagger does not pick up django-rest-registration endpoints.

    Swagger does not pick up django-rest-registration endpoints.

    So I did setup everything like we you have here in example, and it seems like django-rest-swagger does not pick endpoints.

    Here are my urls:

    from django.conf import settings
    from django.urls import path, re_path, include, reverse_lazy
    from django.conf.urls.static import static
    from django.contrib import admin
    from django.views.generic.base import RedirectView
    from rest_framework.routers import DefaultRouter
    from rest_framework.authtoken import views
    from .users.views import UserViewSet, UserCreateViewSet
    
    from rest_framework_swagger.views import get_swagger_view
    
    schema_view = get_swagger_view(title='Luken API')
    
    router = DefaultRouter()
    router.register(r'users', UserViewSet)
    router.register(r'users', UserCreateViewSet)
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('accounts/', include('rest_registration.api.urls')),
        path('api/v1/', include(router.urls)),
        path('api/swagger/', schema_view),
    
        re_path(r'^$', RedirectView.as_view(url=reverse_lazy('api-root'), permanent=False)),
    
    ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    

    Here is the result.

    screen

    If you notice accounts are not being displayed.

    opened by polinom 10
  • Switch to class-based views

    Switch to class-based views

    Class-based views greatly simplify adding extra logic into views. I am therefore proposing to refactor the views into class-based views.

    Here are some examples why this is useful, at least as far as the user registration view is concerned:

    1.) For user registration, I would like to record the client IP address. This is best done in the register view, by calling serializer_class() with an extra context argument, like serializer_class(data=request.data, context={'request': request}). This way, the serializer can access self.context['request'].META.get('REMOTE_ADDR') and populate a field on the model instance it creates.

    As far as I can see, this is currently not possible, except by replacing the view by a copy of it and modifying it. This violates the DRY principle and is also detached from future upstream changes in this view.

    If the registration view was a class-based view inheriting from DRF's GenericAPIView (e.g. a CreateAPIView), the following few lines of code would be sufficient to solve this (assuming the upstream registration view is called DefaultRegisterUserView):

    class RegisterUserView(DefaultRegisterUserView):
        def get_serializer_context(self):
            return {'request': self.request}
    
    class RegisterUserSerializer(DefaultRegisterUserSerializer):
        def create(self, validated_data):
            data = validated_data.copy()
            data['registration_remote_ip'] = self.context['request'].META.get('REMOTE_ADDR')
            return super().create(data)
    

    ... plus the two lines in urls.py and settings.py where the view and the serializer are configured.

    2.) Also, one may consider changing the response of the registration view such that information like "user with this email exists" is not leaked. Instead, 202 Accepted could be an appropriate status code in all cases, with the view redirecting to the password reset view if the email address is already taken, and otherwise sending the regular registration verification email.

    (It would be the frontend's responsibility to react with a proper message, like "We have sent you an email with further instructions", which would be true in all cases.)

    This way, one could expose a unified register+reset endpoint which acts as a general "give me access to an account with this email address" endpoint. The frontend could of course present it in different variations for registration and for password reset purposes.

    I am not proposing that these endpoints be merged (that would be another interesting issue); I am rather making the point that if the registration endpoint was a class-based view, one could easily achieve the above with something like (rough sketch):

    class RegisterUserView(DefaultRegisterUserView):
        def post(request, *args, **kwargs):
            try:
                # Try to register user as usual
                return super().post(request, *args, *kwargs)
            except ValidationError as e:
                # Perform password reset if account exists
                email_error = e.message_dict.get('email')
                if email_error and email_error.endswith(' already exists.'):
                    return PasswortResetView.as_view()(request)
    
                # For other errors, continue as usual
                raise e
    
    type:feature-request release:next-major-version 
    opened by peterthomassen 9
  • *_VERIFICATION_EMAIL_TEMPLATES is invalid

    *_VERIFICATION_EMAIL_TEMPLATES is invalid

    I try to use the package without any TEMPLATES option. I've added in settings minimal REST_REGISTRATION config, but get REGISTER_EMAIL_/REGISTER_VERIFICATION_/RESET_PASSWORD_VERIFICATION_EMAIL_TEMPLATES is invalid (3 SystemCheck errors). Also I've tried to set this options in {}, '', None, but unsuccessfully. Minimal config to launch server:

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'APP_DIRS': True,
        },
    ]
    

    Do I something wrong understand with using the package or Django..?

    type:question 
    opened by ruslankrivoshein 8
  • Using username instead of user_id in signer

    Using username instead of user_id in signer

    I am trying to use username instead of user_id. But i am getting this error File "/home/krishna/Projects/django/cms/account/views.py", line 79, in _calculate_salt if registration_settings.REGISTER_VERIFICATION_ONE_TIME_USE: File "/home/krishna/anaconda3/envs/django/lib/python3.7/site-packages/rest_registration/utils/nested_settings.py", line 38, in __getattr__ self=self, attr=attr)) AttributeError: Invalid REST_REGISTRATION setting: 'REGISTER_VERIFICATION_ONE_TIME_USE' However this is not related to username or userid data but yet i am getting this error. I tried to trackdown this error in nested_settings.py and settings_field.py but there is no problem.

    Also i am using whole view.py from rest-registration/api/views/register.py

    type:feature-request priority:high 
    opened by itzmanish 8
  • Verify email does not seems to behave like expected

    Verify email does not seems to behave like expected

    So I hit resgiter email and receive a following email:

    Please verify your account by clicking on this link:
    
    http://localhost:8000/verify-account/?user_id=cb185c07-143f-4d99-ac64-18a280e221ff&timestamp=1519787713&signature=4uB1gS9O32bPa7skbCvEh88XCec
    

    I want to activate this account, so I take user_id, timestamp signature and email address end send this to our POST /api/v1/accounts/verify-email/

    I get response:

    {
      "detail": "Invalid signature"
    }
    

    And btw,

    http://localhost:8000/verify-account/?user_id=cb185c07-143f-4d99-ac64-18a280e221ff&timestamp=1519787713&signature=4uB1gS9O32bPa7skbCvEh88XCec

    urls that i get in email is not working

    opened by polinom 8
  • Invalid Signature When Verifying Email Address and More Using Postman

    Invalid Signature When Verifying Email Address and More Using Postman

    Checklist

    • [X] I searched existing issues before opening this one
    • [X] I reproduced the bug with the newest version

    Describe the bug

    The views that require posting in a JSON body, such as verify-email, will fail with an Invalid Signature error when using Postman. This has been reported in some form in #11 and #42, but neither had a solution.

    Expected behavior

    Posting into these endpoints succeeds and returns a 2XX error code as expected, like it does with cURL.

    Actual behavior

    The problem appears to occur due to some temporary headers that Postman is setting. This problem has been reported with a number of APIs in other forms in https://community.getpostman.com/t/disable-temporary-headers/5271 and other tickets. After some testing, I believe it comes down to the "Accept-Encoding: gzip, deflate" header. When I include that one with cURL, I receive an error relating to binary data coming back or an invalid signature error. Excluding that single temporary header that Postman sets (which is not supported by the tool itself) allows the request to succeed.

    Steps to reproduce

    Steps to reproduce the behavior:

    1. Create a new request in Postman to POST to the verify-email endpoint with correct data
    2. Send the request and receive an error

    Diagnostic info

    (Please provide below contents of your Django settings.py file (after removing all sensitive information like secrets), or at least provide specific settings mentioned below)

    REST_REGISTRATION = {
        'REGISTER_VERIFICATION_URL': 'https://mysite.example/verify-user/',
        'RESET_PASSWORD_VERIFICATION_URL': 'https://mysite.example/reset-password/',
        'REGISTER_EMAIL_VERIFICATION_URL': 'https://mysite.example/verify-email/',
    
        'VERIFICATION_FROM_EMAIL': '[email protected]',
    
        # Excluding this and using the normal id field does not change the behavior
        'USER_VERIFICATION_ID_FIELD': 'uuid',
    }
    
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'knox.auth.TokenAuthentication',
        ),
        'DEFAULT_PERMISSION_CLASSES': [
            'rest_framework.permissions.IsAuthenticated'
        ],
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE': 25,
        'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
    }
    
    INSTALLED_APPS = (
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'api.apps.ApiConfig',
        'knox',
        'oauth2_provider',
        'rest_framework',
        'rest_registration',
        'rolepermissions',
        'simple_history',
    )
    

    Additional context

    I've also requested that Postman provide a method for excluding temporary headers because of this problem (as have many other people), but I'm not sure what their release timeframe is. Hopefully this is simpler to reproduce and fix on this end.

    type:bug 
    opened by beittenc 7
  • fix profile view with api renderer

    fix profile view with api renderer

    This is fix for bug, that occurs when using rest_framework.renderers.BrowsableAPIRenderer with profile view. It throws following error:

    Traceback (most recent call last):
      File "/home/petr/soubory/programovani/climboard/env/lib/python3.6/site-packages/django/core/handlers/exception.py", line 35, in inner
        response = get_response(request)
      File "/home/petr/soubory/programovani/climboard/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 158, in _get_response
        response = self.process_exception_by_middleware(e, request)
      File "/home/petr/soubory/programovani/climboard/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 156, in _get_response
        response = response.render()
      File "/home/petr/soubory/programovani/climboard/env/lib/python3.6/site-packages/django/template/response.py", line 106, in render
        self.content = self.rendered_content
      File "/home/petr/soubory/programovani/climboard/env/lib/python3.6/site-packages/rest_framework/response.py", line 73, in rendered_content
        ret = renderer.render(self.data, accepted_media_type, context)
      File "/home/petr/soubory/programovani/climboard/env/lib/python3.6/site-packages/rest_framework/renderers.py", line 719, in render
        context = self.get_context(data, accepted_media_type, renderer_context)
      File "/home/petr/soubory/programovani/climboard/env/lib/python3.6/site-packages/rest_framework/renderers.py", line 652, in get_context
        raw_data_put_form = self.get_raw_data_form(data, view, 'PUT', request)
      File "/home/petr/soubory/programovani/climboard/env/lib/python3.6/site-packages/rest_framework/renderers.py", line 555, in get_raw_data_form
        serializer = view.get_serializer(instance=instance)
    TypeError: _get_serializer() got an unexpected keyword argument 'instance'
    
    opened by PetrDlouhy 7
  • Why is there a check for a specific backend?

    Why is there a check for a specific backend?

    At some point a check for a specific authentication backend was introduced. I get this error message:

    django.core.management.base.SystemCheckError: SystemCheckError: System check identified some issues:
    
    ERRORS:
    ?: (rest_registration.E014) invalid authentication backends configuration: LOGIN_DEFAULT_SESSION_AUTHENTICATION_BACKEND is not in AUTHENTICATION_BACKENDS
    

    I implemented my own authentication backend but use rest-registration for the signup process. This error prevents me from upgrading django to a more recent version and is also very restraining. People should be able to use the rest-registration package standalone without it being opinionated about which authentication backend to use.

    This could be changed to a warning to indicate that if you have a problem, you should look into the authentication backends setting.

    type:bug state:needs-answer 
    opened by saevarom 6
  • Return user profile after successful login

    Return user profile after successful login

    With my custom login view I get the user data after successful login via the login endpoint. Unfortunately with django-rest-registration all I get is:

    { "detail": "Login successful" }

    Which means that I have to make another request to the profile endpoint to completely login a user in my frontend. Is this how it should be done? It seems unnecessary to do 2 request if it could be done in one. Curious to hear how others handle this.

    type:feature-request 
    opened by kris7ian 6
  • Default register output serializer should include a token when appropriate

    Default register output serializer should include a token when appropriate

    If the client does not get an AuthToken after registration, the user must provide credentials immediately after registering.

    I was able to add a token to the default serializer with the following custom serializer, but the token should really be created in the view instead.

    from rest_registration.api.serializers import DefaultUserProfileSerializer
    from rest_framework import serializers
    from rest_framework.authtoken.models import Token
    
    
    class MyRegistrationSerializer(DefaultUserProfileSerializer):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.Meta.fields += ('token',)
    
        token = serializers.SerializerMethodField()
    
        def get_token(self, user):
            token = Token.objects.create(user=user)
            return token.key
    

    and in settings.py:

    REST_REGISTRATION = {
    # ...
        'REGISTER_OUTPUT_SERIALIZER_CLASS': '...MyRegistrationSerializer',
    }
    
    closed-as:wontfix 
    opened by ckeeney 6
  • Allow superuser to not be verified

    Allow superuser to not be verified

    Description

    This PR aims to fix #144: when a custom flag field is used, and one tries to change the email of a superuser created by createsuperuser whose flag field is not enabled.

    The strategy employed is to disable user verification in rest_registration.utils.users.get_user_by_lookup_dict if the requested user is a superuser (i.e. is_superuser is True). In other words, a superuser can be unverified.

    Two tests check if, when setting a custom flag field, the email address can be changed for an unverified superuser, and cannot be changed for an unverified normal user.

    Improvements

    Disabling user verification could be made optional. Either with an extra argument to the function (required_verified_superuser defaults to False), or with a settings (SUPERUSER_MUST_BE_VALIDATED defaults to False).

    Why the change is needed?

    See #144.

    Related issues, pull requests, links

    #144

    opened by Neraste 0
  • Cannot change superuser email when using custom flag field and createsuperuser

    Cannot change superuser email when using custom flag field and createsuperuser

    Describe the bug

    I changed the default flag field with USER_VERIFICATION_FLAG_FIELD and created a superuser with the Django command createsuperuser. Changing this superuser's email results in an error when verifying the email.

    Expected behavior

    Superuser email is changed.

    Actual behavior

    Error 400 with the error message: "User not found."

    Steps to reproduce

    1. Create a Boolean field in user model and set USER_VERIFICATION_FLAG_FIELD to this field;
    2. Create a superuser with Django admin command createsuperuser;
    3. Register a new mail for this superuser;
    4. Validate the new email.

    Possible explanation

    This is due to the fact createsuperuser will not enable the custom flag field (as it sets username, email and password by default), hence this superuser is not considered as valid and cannot be retrieved.

    In rest_registration.api.views.register_email.process_verify_email_data, get_user_by_verification_id is called with argument require_verified True by default.

    Associated PR

    See #145.

    type:bug 
    opened by Neraste 3
  • Differentiate bad signature from already used signatures

    Differentiate bad signature from already used signatures

    Checklist

    • [x] I read Contribution Guidelines
    • [x] I searched the documentation to ensure that the requested feature is not already implemented and described
    • [x] I searched existing issues before opening this one

    Is your feature request related to a problem? Please describe.

    From what I read in the source code there are no specific exception when a signature had already been used. It just raise a BadSignature: https://github.com/apragacz/django-rest-registration/blob/master/rest_registration/utils/verification.py#L9 This is because the salt used is not the same after the account had been registered and from the comments this seems by design: https://github.com/apragacz/django-rest-registration/blob/master/rest_registration/api/views/register.py#L46

    The problem is it can lead to a bad user experience. For example, in our setup we have REGISTER_VERIFICATION_AUTO_LOGIN and REGISTER_VERIFICATION_ONE_TIME_USE enabled. If a user follows the verification link on a device, he got registered and logged in. Later on he follows the link on an other device and here we are only able to show a generic error message: "The link is invalid."

    Describe the solution you'd like

    • We would like a specific exception to be raised like SignatureAlreadyUsed OR
    • if there was a way to override the verify_registration endpoint we could first check if a user is verified and if not go on with the usual verification

    In both cases this will allow us to display a relevant error message to our users like "You are already verified, please log in"

    Describe alternatives you've considered

    We tried to enable REGISTER_VERIFICATION_AUTO_LOGIN and disable REGISTER_VERIFICATION_ONE_TIME_USE but this leads to this error:

    REGISTER_VERIFICATION_AUTO_LOGIN is enabled, but REGISTER_VERIFICATION_ONE_TIME_USE is not enabled. This can allow multiple logins using the verification url.

    This is indeed not ideal for security.

    type:feature-request state:research priority:low 
    opened by ppawlak 1
Releases(0.7.3)
CheckList-Api - Created with django rest framework and JWT(Json Web Tokens for Authentication)

CheckList Api created with django rest framework and JWT(Json Web Tokens for Aut

shantanu nimkar 1 Jan 24, 2022
Quick and simple security for Flask applications

Note This project is non maintained anymore. Consider the Flask-Security-Too project as an alternative. Flask-Security It quickly adds security featur

Matt Wright 1.6k Dec 19, 2022
Awesome Django authorization, without the database

rules rules is a tiny but powerful app providing object-level permissions to Django, without requiring a database. At its core, it is a generic framew

1.6k Dec 30, 2022
Authentication for Django Rest Framework

Dj-Rest-Auth Drop-in API endpoints for handling authentication securely in Django Rest Framework. Works especially well with SPAs (e.g React, Vue, Ang

Michael 1.1k Jan 03, 2023
Two factor authentication system using azure services and python language and its api's

FUTURE READY TALENT VIRTUAL INTERSHIP PROJECT PROJECT NAME - TWO FACTOR AUTHENTICATION SYSTEM Resources used: * Azure functions(python)

BHUSHAN SATISH DESHMUKH 1 Dec 10, 2021
Auth for use with FastAPI

FastAPI Auth Pluggable auth for use with FastAPI Supports OAuth2 Password Flow Uses JWT access and refresh tokens 100% mypy and test coverage Supports

David Montague 95 Jan 02, 2023
Python module for generating and verifying JSON Web Tokens

python-jwt Module for generating and verifying JSON Web Tokens. Note: From version 2.0.1 the namespace has changed from jwt to python_jwt, in order to

David Halls 210 Dec 24, 2022
Per object permissions for Django

django-guardian django-guardian is an implementation of per object permissions [1] on top of Django's authorization backend Documentation Online docum

3.3k Jan 01, 2023
Imia is an authentication library for Starlette and FastAPI (python 3.8+).

Imia Imia (belarussian for "a name") is an authentication library for Starlette and FastAPI (python 3.8+). Production status The library is considered

Alex Oleshkevich 91 Nov 24, 2022
Flask Implementation of a login page and some basic functionality.

login_page Flask Implementation of a login page and some basic functionality. How to Run $ chmod +x run.sh setup.sh $ # run setup.sh only if the datab

3 Jun 03, 2021
OAuth2 goodies for the Djangonauts!

Django OAuth Toolkit OAuth2 goodies for the Djangonauts! If you are facing one or more of the following: Your Django app exposes a web API you want to

Jazzband 2.7k Jan 01, 2023
A Python package, that allows you to acquire your RecNet authorization bearer token with your account credentials!

RecNet-Login This is a Python package, that allows you to acquire your RecNet bearer token with your account credentials! Installation Done via git: p

Jesse 6 Aug 18, 2022
Authentication testing framework

What is this This is a framework designed to test authentication for web applications. While web proxies like ZAProxy and Burpsuite allow authenticate

DigeeX 140 Jul 06, 2022
API-key based security utilities for FastAPI, focused on simplicity of use

FastAPI simple security API key based security package for FastAPI, focused on simplicity of use: Full functionality out of the box, no configuration

Tolki 154 Jan 03, 2023
Django CAS 1.0/2.0/3.0 client authentication library, support Django 2.0, 2.1, 2.2, 3.0 and Python 3.5+

django-cas-ng django-cas-ng is Django CAS (Central Authentication Service) 1.0/2.0/3.0 client library to support SSO (Single Sign On) and Single Logou

django-cas-ng 347 Dec 18, 2022
A generic, spec-compliant, thorough implementation of the OAuth request-signing logic

OAuthLib - Python Framework for OAuth1 & OAuth2 *A generic, spec-compliant, thorough implementation of the OAuth request-signing logic for Python 3.5+

OAuthlib 2.5k Jan 01, 2023
Django-react-firebase-auth - A web app showcasing OAuth2.0 + OpenID Connect using Firebase, Django-Rest-Framework and React

Demo app to show Django Rest Framework working with Firebase for authentication

Teshank Raut 6 Oct 13, 2022
RSA Cryptography Authentication Proof-of-Concept

RSA Cryptography Authentication Proof-of-Concept This project was a request by Structured Programming lectures in Computer Science college. It runs wi

Dennys Marcos 1 Jan 22, 2022
OpenConnect auth creditials collector.

OCSERV AUTH CREDS COLLECTOR V1.0 Зачем Изначально было написано чтобы мониторить какие данные вводятся в интерфейс ханипота в виде OpenConnect server.

0 Sep 23, 2022
Python One-Time Password Library

PyOTP - The Python One-Time Password Library PyOTP is a Python library for generating and verifying one-time passwords. It can be used to implement tw

PyAuth 2.2k Dec 26, 2022