iMerica / dj-rest-auth

Authentication for Django Rest Framework
https://dj-rest-auth.readthedocs.io/en/latest/index.html
MIT License
1.65k stars 306 forks source link

Login via Phone and use One Time Password #125

Open tychodev321 opened 4 years ago

tychodev321 commented 4 years ago

Hello,

My team is looking to have users on their mobile devices login with their phone numbers and use a one-time verification code to verify that the login is coming from the right place. Is it a capability that is supported by dj-rest-auth? If not, what would be the right way to go about implementing this with integration to dj-rest-auth?

Thanks,

Steven.

iMerica commented 4 years ago

Since you're asking about phone AND one-time password (not OR), that implies 2FA/MFA which is not a direct feature of this package.

Two approaches to consider:

tychodev321 commented 4 years ago

iMerica,

What would be the best way to integrate django-rest-framework-passwordless with dj-rest-auth? The use case is not 2FA or MPA. Basically our system is used by truckers to move freight, and they are not happy with having to give us their emails and remember a password in order to login. We would like to allow them to login with their phone number, similar to what django-rest-framework-passwordless allows. We are in production and don't want to migrate our authentication system from dj-rest-auth to django-rest-framework-passwordless. Is there any way django-rest-framework-passwordless could be integrated with dj-rest-auth or can this feature be added directly to dj-rest-auth? My team would be happy to contribute if possible.

Thanks,

Steven.

iMerica commented 4 years ago

Hey Steven,

I agree passwordless workflows can be really useful. I went ahead and created a new Github project (kanban board) for this. You can track progress here, if you like.

tychodev321 commented 4 years ago

iMerica,

Thank you for creating the kanban board for this request. Ideally, this enhancement would allow some users to be able to login with a password and others to be able to login in a password-less manner. Please let me know if you need any assistance.

My team also wanted to allow 1 user to be able to login with username/password and then switch to password-less and vice-versa. I am not sure if this is possible, but I am just throwing it out there.

Thanks, Steven.

rollue commented 3 years ago

Any progress here? Really looking forward to this feature 👍

colin-byrne-1 commented 3 years ago

@mhoonjeon It is actually pretty simple to install django-trench (an MFA package), then overwrite the post method of the LoginView of this package. You need to steal the logic from the django-trench login view and use it in your newly-created method.

WP-LKL commented 3 years ago

@mhoonjeon It is actually pretty simple to install django-trench (an MFA package), then overwrite the post method of the LoginView of this package. You need to steal the logic from the django-trench login view and use it in your newly-created method.

Thanks for the tip @cobyrne09. Also, would you happen to have an example you don't mind sharing? I think we can extend it to dj-rest-auth if it doesn't bloat it too much.

colin-byrne-1 commented 3 years ago

@WP-LKL Suspected someone was going to want that... should have posted it earlier! My thoughts... It would be awesome to have that functionality in dj-rest-auth. That said, that project is well-built (it definitely was the best django-based MFA package I found) but poorly maintained, and I'd wager 95% of people using it in production will need to fork it. I don't know standard protocol in such cases, but I would be hesitant to depend on it in any regard.


from dj_rest_auth.registration.views import LoginView
from trench import serializers
from trench.settings import api_settings
from trench.utils import (
    generate_backup_codes,
    get_mfa_handler,
    get_mfa_model,
    user_token_generator,
)
from trench.views.base import RequestMFAMethodActivationView
from trench.views.simplejwt import JSONWebTokenLoginWithMFACode

MFAMethod = get_mfa_model()

class LoginViewCustom(LoginView):
    authentication_classes = ()

    def handle_mfa_response(self, user, mfa_method, *args, **kwargs):
        data = {
            'ephemeral_token': user_token_generator.make_token(user),
            'method': mfa_method.name,
            'other_methods': serializers.UserMFAMethodSerializer(
                user.mfa_methods.filter(is_active=True, is_primary=False),
                many=True,
            ).data,
        }
        return Response(data)

    def post(self, request, *args, **kwargs):
        self.request = request
        self.serializer = self.get_serializer(data=request.data)
        self.serializer.is_valid(raise_exception=True)
        user = self.serializer.user
        auth_method = (
            user.mfa_methods
                .filter(is_primary=True, is_active=True)
                .first()
        )

        // check if user has mfa enabled. In my case, it's optional for login. 
        if auth_method:
            conf = api_settings.MFA_METHODS[auth_method.name]
            handler = conf['HANDLER'](
                user=user,
                obj=auth_method,
                conf=conf,
            )
            handler.dispatch_message()
            return self.handle_mfa_response(user, auth_method)

        self.login()
        return self.get_response()