GithubHelp home page GithubHelp logo

Comments (8)

pyepye avatar pyepye commented on June 1, 2024

Hi @efojs

You could always set LOGIN_REDIRECT_URL to redirect the user to another url which runs the extra checks, but this won't work if the user has had the next query param set when logging in.
With that in mind I really like the idea of being able to run some custom code if the users has been verified, ignoring is next was set or not.

The issue with using the UserPassesTestMixin is it causes a redirect on fail. When I first implemented a form of magic link authentication I simply kicked users who failed authentication to the login page but got feedback that it was rather confusing.
There is also the other issue is that it would not be possible to clear out the cookie created when using REQUIRE_SAME_BROWSER.

What do you think about adding a method onto LoginVerify which you can extend / override to do other actions? It can default to the current redirect action but you can do something different if you would like?

I'm thinking you would just need to do something like:

class CustomLoginVerify(LoginVerify):

    def login_complete_action(self):
        user = self.request.user.email
        some_additional_activity_or_checks(user)
        return redirect_to_say_complete_profile(user)

The LoginVerify code would probably look something like:

class LoginVerify(TemplateView):

    def get(self, request, *args, **kwargs):
        ...
        response = self.login_complete_action(request)
        if settings.REQUIRE_SAME_BROWSER:
            cookie_name = f'magiclink{magiclink.pk}'
            response.delete_cookie(cookie_name, magiclink.cookie_value)
        return response

    def login_complete_redirect(self, request) -> HttpResponse:
        token = request.GET.get('token')
        magiclink = MagicLink.objects.get(token=token)
        return HttpResponseRedirect(magiclink.redirect_url)

    def login_complete_action(self, request) -> HttpResponse:
        return self.login_complete_redirect(request)

It will also probably be a good idea to add a new setting which makes it easily change the verify url so it can easily be pointed at a custom LoginVerify.

from django-magiclink.

pyepye avatar pyepye commented on June 1, 2024

As I had already done half the work above above, I decided to put the changes in a branch, it would be good to get your feedback @efojs - Especially for the README and if it's clear enough how to implement custom code:

master...verify-flow

from django-magiclink.

efojs avatar efojs commented on June 1, 2024

Hi. Thanks for quick response, you are fast) I'll check later today, please don't close it

from django-magiclink.

efojs avatar efojs commented on June 1, 2024

I'm not sure if I get your plan. But maybe you can get mine)

UserPassesTestMixin does not necerelly redirect on fail.
It is raise_exception attribute of AccessMixin (its parent) being False on default causes it.
It makes handle_no_permission method redirect to login page on fail, or raise exception

Depending on the results of test_func one can either customize handle_no_permission (e.g. to cleanup cookies in request) or (as you offer in login_complete_action) customize get before returning super response or redirect to custom path

I mean it looks like you can put your check in test_func and the rest, if needed, can be handled via mixin's methods — no need for custom methods, flows and paths.

Does it make sense? What am I missing in your approach?

from django-magiclink.

pyepye avatar pyepye commented on June 1, 2024

Hey @efojs

I mean it looks like you can put your check in test_func and the rest, if needed, can be handled via mixin's methods — no need for custom methods, flows and paths.

Is there a way to swap the view from LoginVerify to a custom one without the extra setting and path / urls if we use the UserPassesTestMixin? Or do you just mean the custom login_complete_action method isn't needed?
I should note I split login_complete_redirect and login_complete_action so if someone wanted to override login_complete_action but keep the redirect the same they could just call return self.login_complete_redirect()

I believe the issue with your approach is to remove a cookie from the clients machine you need to delete it from a response object not a request object.
On top of this the cookie we are trying to clean up is done if the login is successful (REQUIRE_SAME_BROWSER check).

I wanted to try and make sure my understanding of the flow you are suggesting is correct so I decided to create a new branch which would pass the same tests as the branch I created above.
Most importantly it needs to pass this test - https://github.com/pyepye/django-magiclink/blob/verify-flow/tests/test_login_verify.py#L115 - which both does a custom redirect AND deletes the cookie for a custom verify endpoint..

The diff between the code I suggested above and the UserPassesTestMixin style (no test changes)
verify-flow...verify-flow-mixin

When overriding the get() method you would then either be stuck only redirecting to same redirect response as the LoginVerify get() which isn't very flexible or you would need to manually add the extra code to delete the cookie which I don't think would be very clear?

For example here is the code required:

Custom method
Test view: https://github.com/pyepye/django-magiclink/blob/verify-flow/tests/urls.py#L17-L21
README: https://github.com/pyepye/django-magiclink/blob/verify-flow/README.md#custom-login-verify-flow

UserPassesTestMixin style
Test view: https://github.com/pyepye/django-magiclink/blob/verify-flow-mixin/tests/urls.py#L17-L34
README: https://github.com/pyepye/django-magiclink/blob/verify-flow-mixin/README.md#custom-login-verify-flow

The UserPassesTestMixin approach does allow for a custom fail flow using handle_no_permission but we can easily add a similar method in to match this.

Also as a side point, using the UserPassesTestMixin isn't semantically correct as we are trying to control authentication to the system where as the Mixin is meant to be for checking access / authorization to a specific view. I don't think this is a reason not to use it if it works though.

Does that make sense at all? Hopefully I've misunderstood you and UserPassesTestMixin

Saying all that, I was hoping the README updates would be enough to show how custom code could be run with my approach but it doesn't seem to be. I'll think about what wording I can add to make it more clear.

from django-magiclink.

efojs avatar efojs commented on June 1, 2024

Hi! Excuse me for not responding long. I could not wrap my head around it.
And now, when I came with another request, I can't afford posting, until I respond to this, so I wrapped)

And you are right. Because you offer:

  • reason of failure, which I can't see how to implement with test_func(),
  • redirecting to another URL, despite it looks wrong place, and for sure it was solved before (to complete profile, accept new version of user agreement or else), but I can't think of any solution (except maybe some kind of get_success_url())
    And also I've just realized that I need this solution, because I redirect after calling super().get() (thus not deleting cookie)

So yes, cool, would be glad if you update with it

As for docs/implementation, I think naming is a bit confusing (login_complete_action, login_complete_redirect), idk, maybe because English is not my native

In the example you check is_superuser twice:

if self.request.user.is_superuser:
    url = reverse('superuser_page')
elif self.request.user.is_superuser:
    ...

And not sure if there is a need for MAGICLINK_LOGIN_VERIFY_URL
I mean I use your original names and all works fine
(or why not to customize all names then)

from django-magiclink.

pyepye avatar pyepye commented on June 1, 2024

Hi @efojs

I cleaned up the code a little, removed the confusing login_complete_redirect and fixed the double is_superuser.

It's also now been merged into master so will be in the next release (which I will do later this evening)

Thanks
Matt

from django-magiclink.

pyepye avatar pyepye commented on June 1, 2024

I've created a new release - version 1.1.0 - which has been pushed to Pypi.

from django-magiclink.

Related Issues (14)

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.