Django/Django Rest 如何保存用户设备以防止每次登录时出现繁琐的 2FA?

lor*_*ock 5 python django django-rest-framework

您好,我一直在使用 Django Rest Framework 和 JWT 作为身份验证框架,我成功地基于电子邮件 OTP 进行了双因素身份验证登录,但我想要改进的一件事是我想改进登录并保存用户的设备,以便重复 2FA(双因素身份验证)可以最小化吗?

这是我为在用户电子邮件上发送 otp 所做的某些代码实例。

序列化器.py

class UserLoginSerializer(serializers.Serializer):
    email = serializers.EmailField()
    password = PasswordField()
Run Code Online (Sandbox Code Playgroud)

视图.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()
Run Code Online (Sandbox Code Playgroud)

用例.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')
                }
            )
Run Code Online (Sandbox Code Playgroud)

我很困惑如何允许或保存设备以防止重复 2FA。

New*_*bie 3

太长了;

在非常高的层面上:对 OTP 进行标记(将 替换为otpJWT

解释

AJWT无非是带有一些标准化字段(过期)、(不在之前)等的JSON 签名有效负载。签名(对称或非对称)确保完整性、真实性和不可否认性(例如,令牌是由我们认为的人发行的并且未被更改)。expnbf

JWT有效负载中,您可以放置​​任何内容(请记住该值未加密),包括应用程序逻辑所需的数据。您还可以在不同的身份验证步骤中交换不同的内容JWT

一些例子

延迟 2FA
  • 用户向您提供<username, password>是否有效,type_1_jwt如果启用了 2FA,它会收到一个,否则会收到一个type_2_jwt
  • 用户给你<type_1_jwt, otp>如果两者都有效它会收到一个type_2_jwt
  • type_2_jwt您的应用程序仅在所有其他端点上接受

在这里,您可以在初始用户身份验证后(或者即使检测到可疑设备)灵活地请求 OTP。如果需要 OTP,则用户不需要再次插入密码,而且密码也不会存储在任何地方,也不会在任何未来的请求中重新提交。<username, password, otp>安全级别与同步身份验证保持相同

更新令牌
  • 用户验证并收到renew_jwt(很长一段时间后过期或永远不会)
  • 用户将 换成renew_jwta auth_jwt(快过期)
  • auth_jwt您的应用程序仅在所有其他端点上接受

如果您希望撤销或更改身份验证(更改角色),此解决方案可提供极大的灵活性。您可以auth_jwt在整个应用程序中使用它,但过期速度非常快,然后您就拥有了一个renew_jwt已经用于正确身份验证的交易,并且可以用于自动请求auth_jwt,只有满足某些条件,系统才会满足此请求(例如身份验证尚未撤销)。

全部混合在一起

  • 用户给你<username, password, otp>它收到一个auth_jwt(或一个renew_jwt)和一个otp_jwt
  • 用户给你<username, password, otp_jwt>
  • auth_jwt您的应用程序仅在所有其他端点上接受

第一次身份验证后,用户设备会收到一个otp_jwt(具有任意长的持续时间)并可以存储在设备上(cookie、appStorage、secureStorage...)。这otp_jwt可以在将来的任何时刻使用(与用户名和密码一起)再次进行身份验证。

从概念上讲otp_jwt,令牌是用户过去已成功使用 OTP 进行身份验证的安全证明

假设设备被认为相当安全,这种策略不会显着增加未经授权访问的风险。但请记住,您应该始终为用户提供一个选项,例如completely log out也可以清除此令牌。