Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django/Django Rest How do I save user device to prevent tedious 2FA on every login?

Hello I have been working with Django Rest Framework with JWT as authentication framework and I successfully made Two factor authentication Login based on Email OTP but one thing I want to improve is I want to improve login and save user's device so that repeated 2FA(Two factor Authentcation) can be minimized?

here is certain instance of code I did for sending otp on user email.

serializers.py

class UserLoginSerializer(serializers.Serializer):
    email = serializers.EmailField()
    password = PasswordField()

views.py

class UserLoginView(generics.CreateWithMessageAPIView):
    """
    Use this end-point to get login for  user
    """
    message = _('Please check your email for 6 digit OTP code.')
    serializer_class = serializers.UserLoginSerializer

    def perform_create(self, serializer):
        usecases.UserLoginWithOTPUseCase(self.request, serializer=serializer).execute()

usecases.py

class UserLoginWithOTPUseCase(CreateUseCase, OTPMixin):
    def __init__(self, request, serializer):
        self._request = request
        super().__init__(serializer)

    def execute(self):
        self._factory()

    def _factory(self):
        credentials = {
            'username': self._data['email'],
            'password': self._data['password']
        }
        self._user = authenticate(self._request, **credentials)
        if self._user is not None:
            """
            Sends email confirmation mail to the user's email
            :return: None
            """
            code = self._generate_totp(
                user=self._user,
                purpose='2FA',
                interval=180
            )

            EmailVerificationEmail(
                context={
                    'code': code,
                    'uuid': self._user.id
                }
            ).send(to=[self._user.email])
        else:
            raise PermissionDenied(
                {
                    'authentication_error': _('User name or password not matched')
                }
            )

I am confused how can I allow or save device to prevent repetitive 2FA.

like image 876
nava Avatar asked Oct 15 '22 20:10

nava


People also ask

Which authentication is best in Django REST framework?

Django-Knox is a framework that makes the authentication of the API endpoints built with the Django Rest Framework easier. However, Knox is also a token-based authentication like JSON Web Token (JWT) auth. Django-Knox comes with well-detailed documentation for easy implementation.

How can you secure the Django REST framework based REST API?

User token endpoint The Django rest framework comes with an endpoint that users can use to generate their authentication tokens by providing their valid username and password. In the django_todo project directory add the API endpoint for token generation in the urls.py file.

How do you implement OTP based authentication in Django REST framework?

Step 1: Find that phone number existing in the phone model. Step 2: Generate a key of base32 using base64 library. Step 3: Use the Key to generate an Object of class pyotp. Step 4: Now using the Counter of User model and OTP code sent by the user, validate the authenticity of the user.


2 Answers

TLDR;

At a very high level: tokenize the OTP (exchange the otp for a JWT).

Explanation

A JWT is nothing more than a JSON signed payload with some standardized fields exp (expiration), nbf (not before), etc.... The signature (symmetric or asymmetric) assures integrity, authenticity, and non-repudiation (eg the token has been issued by whom we think and has not been altered).

Within a JWT payload you can put anything (keeping in mind that the value is not encrypted) including data required for your application logic. You can also exchange different JWT during different authentication steps.

Some examples

Deferred 2FA
  • User gives you <username, password> if valid it receives a type_1_jwt if 2FA is enabled or else a type_2_jwt
  • User gives you <type_1_jwt, otp> if both are valid it receives a type_2_jwt
  • Your app accepts only type_2_jwt on all other endpoints

Here you have the flexibility to request the OTP after initial user authentication (or even if a suspicious device is detected). If the OTP is required user is not required to insert the password again but also the password is never stored anywhere nor re-submitted in any future request. The security level is kept the same as <username, password, otp> simultaneous authentication

Renew Token
  • User authenticates and receives a renew_jwt (expires after a long period or never)
  • User exchanges the renew_jwt for a auth_jwt (fast expiring)
  • Your app accepts only auth_jwt on all other endpoints

This solution allows for great flexibility if you want the authentication to be revoked or altered (altering roles). You can have auth_jwt that is used within your entire application but expires very fast, then you have a renew_jwt that has been already traded for proper authentication and can be used to automatically request an auth_jwt, this request will be met by the system only if certain criteria are met (for example authentication has not been revoked).

Mixing all together

  • User gives you <username, password, otp> it receives a auth_jwt (or a renew_jwt) and a otp_jwt.
  • User gives you <username, password, otp_jwt>
  • Your app accepts only auth_jwt on all other endpoints

After the first authentication the user device receives an otp_jwt (that has an arbitrarily long duration) and can be stored on the device (cookies, appStorage, secureStorage, ...). This otp_jwt can be used in any future moment (together with username and password) to authenticate again.

Conceptually the otp_jwt token is a secure proof the user has successfully authenticated with an OTP in the past

Assuming the device can be considered reasonably safe, such strategy does not significantly increase the risk of unauthorized access. But keep in mind that you should always give an option to the user to completely log out eg to clear this token too.

like image 82
Newbie Avatar answered Oct 27 '22 13:10

Newbie


What you can do here is whenever you create a JWT token and set it on user's device for a domain, along with it create a unique key (just an identifier for this device) and set it on the user's device as a cookie for that domain. (save this unique key and user's id map in a db)

Keep the expiry for this cookie different from that of the JWT and do not clean this cookie even if the user logs out. (let it self expire)

So even if the user logs out of the device or jwt expires, in the next login request you will still receive the device cookie you set (lets say you set it for a month). when a login request comes and has a device cookie with it, you can check if this user id and device key is registered with you. If yes you know that this user has logged in with this device in the last one month and you can skip 2FA, else if no such combination exists you can prompt for 2FA.

Additionally lets say the user is logging in for the first time, in that case the device cookie will be missing in the request and you can safely prompt for 2FA.

you can play around with this device cookie's checks like you can also save the timestamp for when this key was created and increase the level of checks.

like image 31
wave Avatar answered Oct 27 '22 12:10

wave