Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better document how the partial pipeline should be handeled #1127

Open
PetrDlouhy opened this issue Mar 10, 2022 · 1 comment
Open

Better document how the partial pipeline should be handeled #1127

PetrDlouhy opened this issue Mar 10, 2022 · 1 comment

Comments

@PetrDlouhy
Copy link

I am trying to get social login working together with django-two-factor-auth (so that it requires additional authentication step for users with 2FA enabled).
I am now stuck for several hours on this step from documentation: Partial Pipeline

There is written:

When it’s time to resume the process just redirect the user to /complete// or /disconnect// view. The pipeline will resume in the same function that cut the process.

I have already figured out, that it is not very precise description, because I have to pass the partial_token token to that URL.
But I am still stuck on the fact, that the pipeline is returning to the same (two factor authentication) step again and again.
To overcome this I am trying to store somewhere the information that the user has been authenticated by 2FA, but I don't know how to access the pipeline correctly or if I am not doing it completely wrong.

I would be glad if this is documented more deeply, possibly with some examples. I could also help to extend the documentation if I know how to implement this correctly.

@PetrDlouhy
Copy link
Author

PetrDlouhy commented Mar 10, 2022

I have got this solved by storing the info about 2FA authentication in session. But I feel it is a bit hacky and unneedingly complicated.

The pipeline:

from django.shortcuts import redirect
from django.urls import reverse
from social_core.pipeline.partial import partial
from two_factor.utils import default_device


@partial
def two_factor_auth(strategy, details, *args, user=None, **kwargs):
    current_partial = kwargs.get("current_partial")
    request = kwargs["request"]
    if request.session.get("tfa_completed", False):
        return
    if default_device(user):
        return redirect(
            reverse("two_factor_authentication") + f"?partial_token={current_partial.token}"
        )
    return

The view:

from django.urls import reverse
from django.views.generic import FormView
from social_django.utils import load_strategy
from two_factor.forms import AuthenticationTokenForm
from two_factor.utils import default_device


class AuthenticationView(FormView):
    template_name = "two_factor/core/login.html"
    form_class = AuthenticationTokenForm
    
    def get_success_url(self):
        partial = self.get_partial()
        self.request.session["tfa_completed"] = True
        return (
            reverse("social:complete", kwargs={"backend": partial.backend})
            + f"?partial_token={partial.token}"
        )

    def get_partial(self):
        strategy = load_strategy()
        partial_token = self.request.GET.get("partial_token")
        partial = strategy.partial_load(partial_token)
        return partial

    def get_form_kwargs(self, *args, **kwargs):
        kwargs = super().get_form_kwargs(*args, **kwargs)
        user = self.get_partial().kwargs["user"]
        kwargs["user"] = user
        kwargs["initial_device"] = default_device(user)
        return kwargs

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["step"] = "auth"
        return context

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant