Giter VIP home page Giter VIP logo

dj-rest-auth's Introduction

Dj-Rest-Auth

<iMerica>

Drop-in API endpoints for handling authentication securely in Django Rest Framework. Works especially well with SPAs (e.g., React, Vue, Angular), and Mobile applications.

Requirements

  • Django 3, 4 and 5 (See Unit Test Coverage in CI)
  • Python >= 3.8

Quick Setup

Install package

pip install dj-rest-auth

Add dj_rest_auth app to INSTALLED_APPS in your django settings.py:

INSTALLED_APPS = (
    ...,
    'rest_framework',
    'rest_framework.authtoken',
    ...,
    'dj_rest_auth'
)

Add URL patterns

urlpatterns = [
    path('dj-rest-auth/', include('dj_rest_auth.urls')),
]

(Optional) Use Http-Only cookies

REST_AUTH = {
    'USE_JWT': True,
    'JWT_AUTH_COOKIE': 'jwt-auth',
}

Testing

Install required modules with pip install -r dj_rest_auth/tests/requirements.pip

To run the tests within a virtualenv, run python runtests.py from the repository directory. The easiest way to run test coverage is with coverage, which runs the tests against all supported Django installs. To run the test coverage within a virtualenv, run coverage run ./runtests.py from the repository directory then run coverage report.

Tox

Testing may also be done using tox, which will run the tests against all supported combinations of Python and Django.

Install tox, either globally or within a virtualenv, and then simply run tox from the repository directory. As there are many combinations, you may run them in parallel using tox --parallel.

The tox.ini includes an environment for testing code coverage and you can run it and view this report with tox -e coverage.

Linting may also be performed via flake8 by running tox -e flake8.

Documentation

View the full documentation here: https://dj-rest-auth.readthedocs.io/en/latest/index.html

Acknowledgements

This project began as a fork of django-rest-auth. Big thanks to everyone who contributed to that repo!

A note on Django AllAuth from @iMerica

This project has optional and very narrow support for Django-AllAuth. As the maintainer, I have no interest in making this package support all use cases in Django-AllAuth. I would rather focus on improving the quality of the base functionality or focus on OIDC support instead. Pull requests that extend or add more support for Django-AllAuth will most likely be declined. Do you disagree? Feel free to fork this repo!

dj-rest-auth's People

Contributors

akay7 avatar alichass avatar c-w avatar caruccio avatar gbezyuk avatar illagrenan avatar imerica avatar jasinai avatar jerinpetergeorge avatar jgr3go avatar jmb avatar kiraware avatar lancemoe avatar maciej-jaworski avatar mariodev avatar mateusz-sikora avatar maxim-kht avatar mjaworski avatar mjlabe avatar mohmyo avatar mpuhacz avatar n1207n avatar noamkush avatar null-none avatar philippeluickx avatar ron8mcr avatar steverecio avatar tevinjoseph avatar tgamauf avatar wolf-byte avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dj-rest-auth's Issues

JWT Refresh Token for Cookie Authentication

Hey there

I'm using the JWT Cookie authentication setup and just have a question as to how an access token can be refreshed?

The simplejwt package has an endpoint for refreshing the access token but it's expecting you to pass it the refresh token. But if you're storing the JWT in a cookie then you can't access the refresh token from it, so how do you go about it?

Or am I misunderstanding it completely? If so, please enlighten me!

Thanks

Implementing Registration info in Docs

I made a clean project, started with implementing this app following the docs but I was fighting a bit with implementing Registration with allauth, since I was getting 500 all the time.

The error was when I was sending first POST request. App was failing with 500 but user got registered. Each consequent request was returning validation for registered user exist which is OK.

The problem was with setting up the Email for sending the activation link.
This solved my problem.

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Do you think docs should be updated with this info?

How to customize the url in password_reset_confirm.html

I appreciate you forking and maintaining this project!

I have my api hosted at a different location (completely different url) than my frontend. When I send the password reset confirm email I'd like to have the url point to one on my front end which can be parsed and displayed there. I can't figure out where the email content is generated.

I see this line

# this url is used to generate email content url(r'password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', TemplateView.as_view(template_name="password_reset_confirm.html"), name='password_reset_confirm'),

so I understand that is where the email url is being generated from. I can't figure out where to customize this email. I tried searching but forked repos aren't able to search code. I tried searching the original repo but still couldn't find it.

Thanks!

Email template issue

I am sending mail for email verification. I have override the templates. The templates works fine if we register with email from temp mail but if we use an authentic email id from gmail, then it shows raw html in the email. Please can you help me figure this out ASAP

Encapsulate all views into a viewset

It's better to let the user know, what API(s) we have in the API root page, so we should encapsulate all views into one viewset object.People just need to register it in the drf router.

New releases is not in the repo

Why new releases isn't announced here?

I have installed it when it was at version 0.1.4 and turned on the repo watch releases notifier.
Since then I was tracking repo progress, I loved the way it's being active, didn't see any new releases here but guessed it was waiting for a major one.

Now by coincidence when I was checking my outdated pip packages found out that there was already a new release with version 1.0.0 !! WOW

So, to avoid this I think we need to include new releases here

Thanks.

Task: Release Version 0.2.0

Checklist:

  • Do some end user testing/QA.
  • Add entry to the changelog.
  • Add a http-only jwt cookie example client in the demo.
  • Add some comments to the docs about jwt changes and http-only cookies.

Add new endpoint to verify password reset confirm token

Hello,

For web clients it's essential to have an endpoint to verify password reset confirm token, so it can easily and securely routed to the password reset confirm form page and render it for a user to interact with.

CSRF Login vulnerability for web login (and other unauthenticated post requests)

The following test case demonstrates the issue (this test will fail without csrf checks)

@override_settings(REST_USE_JWT=True)
@override_settings(JWT_AUTH_COOKIE='jwt-auth')
def test_login_csrf(self):
    from rest_framework.test import APIRequestFactory
    from django.contrib.sessions.middleware import SessionMiddleware
    from dj_rest_auth.registration.views import LoginView

    get_user_model().objects.create_user(self.USERNAME, '', self.PASS)
    view = LoginView.as_view()

    def add_session_to_request(request):
        """Annotate a request object with a session"""
        middleware = SessionMiddleware()
        middleware.process_request(request)
        request.session.save()

    factory = APIRequestFactory(enforce_csrf_checks=True)
    request = factory.post(self.login_url, {
        "username": self.USERNAME,
        "password": self.PASS
    })
    add_session_to_request(request)
    response = view(request)
    self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

There are 2 issues:

  1. the login (and other unauthenticated post) forms have csrf when using rest_framework.authentication.SessionAuthentication'.

  2. the login (and other unauthenticated post) forms have csrf when using JWTCookieAuthentication

1 can be fixed with using different views for web clients that only use SessionAuthentication. 1 is not possible to fix as a single view because of how drf handles multiple auth backends (see notes below).

2 can be fixed with the following:

from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework import exceptions
from rest_framework.authentication import CSRFCheck
class JWTCookieAuthentication(JWTAuthentication):
    """
    An authentication plugin that hopefully authenticates requests through a JSON web
    token provided in a request cookie (and through the header as normal, with a
    preference to the header).
    """
    def enforce_csrf(self, request):
        """
        Enforce CSRF validation for session based authentication.
        """
        check = CSRFCheck()
        # populates request.META['CSRF_COOKIE'], which is used in process_view()
        check.process_request(request)
        reason = check.process_view(request, None, (), {})
        if reason:
            # CSRF failed, bail with explicit error message
            raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)

    def authenticate(self, request):
        from django.conf import settings
        cookie_name = getattr(settings, 'JWT_AUTH_COOKIE', None)
        header = self.get_header(request)
        self.enforce_csrf(request)
        if header is None:
            if cookie_name:
                raw_token = request.COOKIES.get(cookie_name)
            else:
                return None
        else:
            raw_token = self.get_raw_token(header)

        if raw_token is None:
            return None

        validated_token = self.get_validated_token(raw_token)
        return self.get_user(validated_token), validated_token

Django rest framework explicitly disables csrf checks for unauthenticated post requests because of the way django rest works with multiple authentication classes, ie, if you have both Session and Token auth classes for the same login view, its not possible to enforce csrf on login with breaking the Token auth; clients that use token auth may not be web clients and wouldn't have the csrf vulnerability because they are not using cookies.

The built in django login views (django.admin.contrib.auth.views) all enforce csrf on login and other unauthenticated post requests.

The django rest framework recommends using django's login forms for this reason:
https://www.django-rest-framework.org/api-guide/authentication/

Warning: Always use Django's standard login view when creating login pages. This will ensure your login views are properly protected.

CSRF validation in REST framework works slightly differently to standard Django due to the need to support both session and non-session based authentication to the same views. This means that only authenticated requests require CSRF tokens, and anonymous requests may be sent without CSRF tokens. This behaviour is not suitable for login views, which should always have CSRF validation applied.

Login CSRF is explicitly outlined in owasp.org guidelines and should be addressed.
https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#login-csrf

Rest-auth :Exlamation mark comes before password

I am using rest-auth for authentication. after registration when I try to login it show

{
"non_field_errors": [
"Unable to log in with provided credentials."
]
}
and when I saw the user on admin panel an an '!' mark comed before password for example password ='!72wlGF0RiGRraz69sveb63FUrebNkAW9xmOoL16C' please help

PasswordChangeSerializer does not confirm submitted password is correct

During a QA pass of integrating dj-rest-auth, I noticed that I could change my password if I submitted a request with valid session/csrf info yet provided an incorrect password.

In reading the relevant serializer this function - never appears to be called and seems to be related to exactly this behaviour.

I want to check that I am understanding things correctly before attempting to write a PR to include it.


Ah - it appears that I may have been missing setting OLD_PASSWORD_FIELD_ENABLED.

Docs Improvement: Register users from admin panel in a compatible way.

Hi, I went through the documentation for this library. But didn't find an answer for my question.

So I implemented dj-rest-auth and everything is smooth AF.
Thank you so much for this amazing library.

So a user can register via my web app..but I want to be able to add users from my admin application. How can I go about doing that. I have User registered to admin app, but I guess that's not enough.

What are the next steps I need to follow.

Bad request on dj-rest-auth/facebook/

Hi, i'm having a problem with facebook auth.
I'm getting a token from a test user in facebook dev dashboard and trying to create a new user from it (the token is from facebook api v5.0 and i tried with v7.0 too, both gives the same error). I'm sending the token in dj-rest-auth/facebook but it gives the following error:

HTTP 400 Bad Request
Allow: POST, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "non_field_errors": [
        "User is already registered with this e-mail address."
    ]
}

When i tried to use the allauth default "accounts/login/" it works normally. That probably means the facebook is configured correctly.

Here is my url.py:

from django.conf.urls import url, include
from .views import FacebookLogin
from allauth.account.views import ConfirmEmailView

urlpatterns = [
    path('accounts/', include('allauth.urls')),
    path('dj-rest-auth/', include('dj_rest_auth.urls')),
    path('dj-rest-auth/registration/', include('dj_rest_auth.registration.urls')),
    path('dj-rest-auth/facebook/', FacebookLogin.as_view(), name='fb_login'),
]

and my views.py:

from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
from dj_rest_auth.registration.views import SocialLoginView

class FacebookLogin(SocialLoginView):
    adapter_class = FacebookOAuth2Adapter

I don't have registered users.

Does using rest-auth effect any of all-auth other functionality?

Hello,

Thanks for reviving this project. It is very helpful.
Quick question I see all-auth is a dependency for social auth, which is fine, but I am also using all-auth because of its user invitation feature. Is that something that will still work if I add dj-rest-auth?

If so has that feature been added to the rest auth functionality? Or would that need to still use all auth traditional forms?

Any insights you can provide would be much appreciated.

Simple JWT refresh token endpoint

There is no refresh endpoint that works for simple jwt to return and set the new access token cookie. I created my own by extending the simple jwt refresh endpoint. If I have time I'll make a PR but wanted to bring it up to the maintainers here that its needed.

TypeError: 'str' object is not callable - When setting custom UserDetailsSerializer

I've added my own UserDetailsSerializer to my project and copied the default code into my project but get this error when i login.

To start with my CustomUserDetailsSerializer is copied right from the dj_rest_suth source, just to get it working

django.request ERROR    Internal Server Error: /rest-auth/google/
Traceback (most recent call last):
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/django/views/generic/base.py", line 71, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/django/views/decorators/debug.py", line 76, in sensitive_post_parameters_wrapper
    return view(request, *args, **kwargs)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/dj_rest_auth/views.py", line 46, in dispatch
    return super(LoginView, self).dispatch(*args, **kwargs)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/rest_framework/views.py", line 505, in dispatch
    response = self.handle_exception(exc)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/rest_framework/views.py", line 465, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/rest_framework/views.py", line 476, in raise_uncaught_exception
    raise exc
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/rest_framework/views.py", line 502, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/dj_rest_auth/views.py", line 107, in post
    return self.get_response()
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/dj_rest_auth/views.py", line 85, in get_response
    response = Response(serializer.data, status=status.HTTP_200_OK)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/rest_framework/serializers.py", line 562, in data
    ret = super().data
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/rest_framework/serializers.py", line 260, in data
    self._data = self.to_representation(self.instance)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/rest_framework/serializers.py", line 529, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/rest_framework/fields.py", line 1905, in to_representation
    return method(value)
  File "/Users/adam/dev/web/rwe/.venv/lib/python3.7/site-packages/dj_rest_auth/serializers.py", line 149, in get_user
    user_data = JWTUserDetailsSerializer(obj['user'], context=self.context).data
TypeError: 'str' object is not callable
class CustomUserDetailsSerializer(serializers.ModelSerializer):
    class Meta:
        model = user_model
        fields = ('id', 'username', 'email', 'first_name', 'last_name')
        read_only_fields = ('email',)

Settings for Simple JWT

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(days=1),
    'REFRESH_TOKEN_LIFTIME': timedelta(days=3),
    'ROTATE_REFRESH_TOKEN': True,  # Issue a new refresh token with a new access token
    'BLACKLIST_AFTER_ROTATION': True,  # Blacklist old/used refresh tokens

    'ALGORITHM': 'RS512',  # RSA private/public key pair
    'SIGNING_KEY': os.getenv('PRIVATE_KEY'),  # Private RSA key
    'VERIFYING_KEY': os.getenv('PUBLIC_KEY'),  # Public RSA key

    'AUTH_HEADER_TYPES': 'Bearer',
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',

    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
    'TOKEN_TYPE_CLAIM': 'access',  # Type of token to allow for auth ( access, sliding, refresh )

    'JTI_CLAIM': 'jti',
}

REST_AUTH_SERIALIZERS = {
    'USER_DETAILS_SERIALIZER': 'backend.core.serializers.CustomUserDetailsSerializer',
}

I've discovered if I create a custom JWTSerializer and use that the error disappears, as well as everything working as expected if i do not define any custom serializers:

REST_AUTH_SERIALIZERS = {
    'JWT_SERIALIZER': 'backend.core.serializers.CustomJWTSerializer',
    'USER_DETAILS_SERIALIZER': 'backend.core.serializers.CustomUserDetailsSerializer',
}
class CustomUserDetailsSerializer(serializers.ModelSerializer):
    class Meta:
        model = user_model
        fields = ('id', 'username', 'email', 'first_name', 'last_name')
        read_only_fields = ('email',)


class CustomJWTSerializer(serializers.Serializer):
    """
        Serializer for JWT authentication.
        """
    access_token = serializers.CharField()
    refresh_token = serializers.CharField()
    user = serializers.SerializerMethodField()

    def get_user(self, obj):
        """
        Required to allow using custom USER_DETAILS_SERIALIZER in
        JWTSerializer. Defining it here to avoid circular imports
        """
        JWTUserDetailsSerializer = CustomUserDetailsSerializer
        user_data = JWTUserDetailsSerializer(obj['user'], context=self.context).data
        return user_data

The generated password reset token is not long enough and always having a fixed length

I have added password_reset_confirm URL into my urls.py as mentioned in docs, inspired by the demo project

re_path(r'^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
            TemplateView.as_view(),
            name='password_reset_confirm'),

Everything is working just fine, but I have a question regarding the token, I expected it to be longer as the one in registration endpoint but I always get something like that:

/password-reset/confirm/Mw/5f8-8a381855ed4a6cadb9fe/

So, the first part always has the length of 3 characters and the second one always has the length of 20 characters, so, what is the point of giving it a range if it will always have a fixed length, also why it's not long enough, am I missing something?

Can somebody explain me how CSRF are validated?

I can't find anywhere in the documentation how CSRF are validated, but by inspecting the Headers,
csrftoken and sessionid are still send.

Usually, you would place {% csrf_token %} in the form.

UID for password reset incorrectly uses base64

When a password reset link is request, the UID is encoded into the url as such, by django allauth

/accounts/password/reset/key/7w-1x7-d1a11z1sa1s1a1s/

Such that the uid is 7w and the token is 1x7-d1a11z1sa1s1a1s

The UID is encoded here, as base36:
https://github.com/pennersr/django-allauth/blob/330bf899dd77046fd0510221f3c12e69eb2bc64d/allauth/account/forms.py#L524

However, when the UID decoded in django-rest-auth as base64
https://github.com/jazzband/dj-rest-auth/blob/02c9242e3d069164692ef44a0f15eaca31a41cac/dj_rest_auth/serializers.py#L214

pt_BR localization messages not found

Please, take a look at locale\pt_BR\LC_MESSAGES. There is no django.mo binary file, so it doesn't work for this language. Could you please compile and include this file?

Reverse for 'socialaccount_connections' not found

While using SocialConnectView to link a social account to the authenticated user, it raises NoReverseMatch when socialaccount_connections is not set

django.urls.exceptions.NoReverseMatch: Reverse for 'socialaccount_connections' not found. 'socialaccount_connections' is not a valid view function or pattern name

Although the URL can be added using TemplateView as a temporary solution, the library being a complete REST API implementation of the allauth, this exception can be handled and skipped in the library.

question: logging in a user through dj-rest-auth via django-allauth perform_login vs django_login

hi! while attempting to use the user_logged_in signal from allauth, since it did not fire, i had to look at the source code and i found that in LoginView the django-allauth login function is not used rather it is used the django_login one. thus i connected my receiver to the django signal and all was fine. just for information, what has been the thinking about this decision (django login vs allauth)? cheers!

Registration Error: [Errno 111] Connection refused

There is a problem in the part of the documentation described as registration.
I am trying to make the user a member using the 'dj-rest-auth /registration/' url. My purpose is to be able to login, logout and register users using simplejwt. However, at this stage, the application automatically tries to send email and gives an error.

I tried to reach the source of the problem in order to help. allauth is fully refused while trying to send email here (at line 3):

  • allauth/accounts/adapter.py
    def send_mail(self, template_prefix, email, context):
        msg = self.render_mail(template_prefix, email, context)
        msg.send()

Afterwards, when I looked through the admin panel, I saw that the user was a member, but the tokens were not processed in the token table. Then, when I tried /dj-rest-auth/login/ url, I got 'access', 'refresh' tokens and user information as a response. Every time I log in, different tokens come in and data cannot be received with these tokens.

And again afterwards, when I try to delete the user from the admin panel, I get the error:

no such table: allauth_socialaccount

It asks me to specify this application in the settings file; which I thought we used it in social authentication, I was a bit confused about it. When I added 'allauth.socialaccount' to 'INSTALLED_APPS' in setttings file, the problem was solved, so I was able to delete the user from the admin panel.

So there is a big chain of problems here.

  1. register (partly making a member, then it gets refused when sending email and gives error)

  2. login (gives the user different tokens that are not kept in the database in each login and data cannot be received)

  3. OperationalError while user deletion in django admin panel (instructions written in the registration section of the documentation does not work )

How to customize Responses?

Hi everyone,

I can be considered new to Django and trying to figure out how I could modify the existing return values of dj-rest-auth.

One idea might be, creating a custom view by inheriting the existing view but I would like to change all the responses and that option did not sound very clever. Is there any other way that you could recommend ?

Thanks,
Bugra

Feature: The DJ-Auth Protocol

Background

Currently developers need to choose how to persists access tokens on the client. Due to the number of trade-offs, there is no best practice that applies to all scenarios, however my belief is that for most cases, Http-only cookies appear to be the most secure mechanism.

With http-only cookies, Javascript is forbidden from access. This means XSS attacks would fail to steal an access token using existing DOM APIs.

However, one huge problem with this strategy is mobile safari. There are two problematic scenarios that keep popping up:

  • The end-user has saved the site as a Home-Screen shortcut from the Mobile Safari bottom tray (lots of quirks with this)
  • The end-user has disabled cookies in iOS Mobile Safari settings (increasingly becoming very common).

In both of these scenarios, the new http-only cookie becomes less appealing than just using local storage. What if we could use http-only cookies by default and gracefully degrade to localStorage when cookies aren't an option?

Proposed Protocol

Scenario: Obtaining a token

download


Scenario: Authentication

download-1

Implementation

Enable this mode with a configuration setting. Maybe something like:

REST_AUTH_FLEXIBLE_AUTH_MODE = True

When this flag is true, the DEFAULT_AUTHENTICATION_CLASSES is overridden with a single class that looks something like this:

from django.conf import settings
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication

class JSONWebTokenAuthenticationCookie(BaseJSONWebTokenAuthentication):

    def get_jwt_value(self, request):
        for fn in self.strategies:
            cookie = fn(request)
            if cookie:
                return cookie
        return None

    @property
    def strategies(self):
        """ The strategies in order of priority """
        return [
            self.cookie_strategy,
            self.auth_header_strategy
        ]

    @staticmethod
    def cookie_strategy(request):
        return request.COOKIES.get(settings.JWT_AUTH.get('JWT_AUTH_COOKIE'))

    @staticmethod
    def auth_header_strategy(request):
        auth = request.headers.get('Authorization') or ''
        return auth.partition("Token ")[2]

ImportError: Could not import 'dj_rest_auth.utils.JWTCookieAuthenticationrest_framework.authentication' for API setting 'DEFAULT_AUTHENTICATION_CLASSES'. ModuleNotFoundError: No module named 'dj_rest_auth.utils.JWTCookieAuthenticationrest_framework'; 'dj_rest_auth.utils' is not a package.

SITE_ID = 1
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
INSTALLED_APPS += ['django_extensions']

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
'dj_rest_auth.utils.JWTCookieAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}

RemovedInDjango40Warning: django.utils.translation.ugettext_lazy

Just making an issue so I don't forget, but a heads up that I'm getting this warning in my test suite:

RemovedInDjango40Warning: django.utils.translation.ugettext_lazy() is deprecated in favor of django.utils.translation.gettext_lazy().

I'm not sure about the backwards compatibility for this project so I'm not positive this is a one-line change. I'd be more than happy to make a PR with some guidance (but feel free to fix yourselves if that's your preference!)

Implement Jazzband guidelines for dj-rest-auth

This issue tracks the implementation of the Jazzband guidelines for the project dj-rest-auth

It was initiated by @iMerica who was automatically assigned in addition to the Jazzband roadies.

See the TODO list below for the generally required tasks, but feel free to update it in case the project requires it.

Feel free to ping a Jazzband roadie if you have any question.

TODOs

Project details

Description Authentication for Django Rest Framework
Homepage https://dj-rest-auth.readthedocs.io/en/latest/index.html
Stargazers 0
Open issues 0
Forks 0
Default branch master
Is a fork True
Has Wiki True
Has Pages False

Why SocialConnectView is extending LoginView

I believe SocialConnectView is meant to be used to connect social accounts after the user is authenticated.

Extending LoginView is generating token again while connecting a social account which is not required.

email change endpoint

Hi all - is there a recommended way of providing an email change endpoint to the client?

Modified user model

Hi! I've modified the django default user (I've defined a model manager for User model with no username field). I'm getting therefore the following error:

django.core.exceptions.FieldDoesNotExist: User has no field named 'username'

Any suggestions on how to proceed? many thanks!

ImportError: djangorestframework_jwt needs to be installed

When using rest_framework_simplejwt, I am getting ImportError: djangorestframework_jwt needs to be installed when I hit login or registration.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework_simplejwt.token_blacklist',
    'dj_rest_auth',
    'django.contrib.sites',
    'allauth',
    'allauth.account',
    'dj_rest_auth.registration',
    ...
]

REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    )
}

REST_USE_JWT = True

PasswordResetConfirmSerializer validations order should be changed

Hi,

We were integrating our backend with frontend on the password reset part, our frontend dev started to test password/reset/confirm/ without even making a new password reset request, instead, she used an old and expired token from a dummy example, I was surprised that the feedback from her side was that the password validation error messages are confusing not that the endpoint raised an error that token was not valid.

I dug in validate() and found that token validation was the last thing happing on the validation checklist.

I find this very odd, there are so many reasons why token validation should be the first thing to do, for example without even thinking about security and authorization point of view, I can hit password/reset/confirm/ for hours and keep the server busy to validate my password pair and I don't even have a valid token.

So, I suggest token validation take place before anything else.

Doing it should be easy, just put this condition before anything else, but I thought it's better to discuss it here first

Empty email with Facebook registration

When registering a new account using FacebookOAuth2Adapter the email is being saved as an empty string.

In [1]: from app.models import User                                                                                                                                                

In [2]: user = User.objects.last()                                                                                                                                                       

In [3]: user.email                                                                                                                                                                         
Out[3]: ''

There is an open issue about the same situation in django-rest-auth, but since it is not being maintained I thought about posting here.

Missing import_callable statement in app_settins.py

PasswordResetConfirmSerializer = serializers.get(
    'PASSWORD_RESET_CONFIRM_SERIALIZER', DefaultPasswordResetConfirmSerializer
)

In app_settings.py should be:

PasswordResetConfirmSerializer = import_callable(serializers.get(
    'PASSWORD_RESET_CONFIRM_SERIALIZER', DefaultPasswordResetConfirmSerializer
))

State of migrating issues from django-rest-auth to here?

Firstly, thank you very much for taking this repository on. Much appreciated.

This might be more a bring-to-Github-higher-up question, but has there been any overtures to the owners of the upstream repository or Github itself to migrate the issues from that repository to here? One of the values of that repo is the issue tracker and history. There are apparently 137 open and 238 closed tickets on the upstream repo, and manually duplicating here would be very time-consuming.

ERROR: Could not find a version that satisfies the requirement dj-rest-auth==0.9.5 (demo project setup)

Hello.

When I try to install the requirements for the demo project, I get the following error:

ERROR: Could not find a version that satisfies the requirement dj-rest-auth==0.9.5 (from -r requirements.pip (line 2)) (from versions: 0.1.0, 0.1.1, 0.1.2, 0.1.3, 0.1.4)
ERROR: No matching distribution found for dj-rest-auth==0.9.5 (from -r requirements.pip (line 2))

When I install using pip install dj-rest-auth, pip installs dj-rest-auth-0.1.4. Not sure why this is but thought to bring it to your attention.

Thank you for taking up the maintenance of this project and I'll be happy to open a pull request to fix this.

ImproperlyConfigured registration email verification

hi! when I try to access the following link in the email confirmation sent on user registration:

http://localhost:8000/dj-rest-auth/registration/account-confirm-email/MQ:1jIgPr:Ayckd0pouL4B-foYgl2wjdSCYOY/

I receive the following error:

ImproperlyConfigured
TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()'

Where should I create such a template? how should I name the template? it would be great if you could point me to some documentation or shed some light on how to proceed further. cheers!

Customizing JWT claims

Currently SimpleJWT does not have a simple way of overriding the default TokenObtainPairSerializer in its app settings. This makes it somewhat difficult to customize the returned claims in dj-rest-auth without overriding LoginView and the jwt_encode function.

I believe a quick fix would be to allow for a new setting in dj-rest-auth, with the default set to 'rest_framework_simplejwt.serializers.TokenObtainPairSerializer':

REST_AUTH_SERIALIZERS = {
    ... ,
    'JWT_TOKEN_OBTAIN_PAIR_SERIALIZER' : 'someapp.serializers.MyTokenObtainPairSerializer',
    ... ,
}

with 'someapp.serializers.MyTokenObtainPairSerializer' defined as in https://django-rest-framework-simplejwt.readthedocs.io/en/latest/customizing_token_claims.html

I think this would then only require the jwt_encode function to be changed as such but I am unsure whether this action is better suited to within app_settings.py:

from app_settings import serializers as appserializers
def jwt_encode(user):
    try:
        from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
        TOPS = import_callable(appserializers.get('JWT_TOKEN_OBTAIN_PAIR_SERIALIZER', TokenObtainPairSerializer))
    except ImportError:
        raise ImportError("rest-framework-simplejwt needs to be installed")

    refresh = TOPS.get_token(user)
    return refresh.access_token, refresh

Discord Refresh Token and Expiration Date not saved

Not sure if this is my fault, a dj-rest-auth problem, or a django-allauth problem, but getting a code from Discord's callback URL and passing it to /dj-rest-auth/discord saves everything properly, except there is no refresh token or expiration date saved in the Social Tokens table.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.