Repository: mdn/kuma

Issue:
T - Fix post-sign-ins redirect so that user lands on correct page
**Summary**
You're not taken back to where you were when you sign in. At least via the modal and GitHub.

![Screen-Recording-2020-04-03-at-2 31 33-PM](https://user-images.githubusercontent.com/26739/78393569-167dd900-75b8-11ea-9d78-3bf0ca358bb2.gif)


**Steps To Reproduce (STR)**


1. Go to some page other than the home page. Be not-signed in.
2. Click sign in. 
3. Click GitHub and complete your GitHub auth stuff


**Actual behavior**
I ended up back on the home page `/en-US/`. :(


**Expected behavior**
To be taken to the page I was originally on. 


**Additional context**
Pretty sure this is regression but don't know if it started when we switched to a modal. 



Discussion:
Definitely something about the modal. If you're on some non-homepage page and right click the "Sign in" which takes you to a page like:
http://localhost.org:8000/en-US/users/account/signup-landing?next=/en-US/docs/Web/API
then, after you sign in, the redirect redirects you back as expected. 

Resolving patch:
diff --git a/kuma/core/context_processors.py b/kuma/core/context_processors.py
--- a/kuma/core/context_processors.py
+++ b/kuma/core/context_processors.py
@@ -4,6 +4,8 @@
 from django.conf import settings
 from django.utils import translation
 
+from kuma.core.urlresolvers import reverse
+
 from .i18n import get_language_mapping
 
 
@@ -54,10 +56,30 @@ def i18n(request):
 
 
 def next_url(request):
-    if (
-        hasattr(request, "path")
-        and "login" not in request.path
-        and "register" not in request.path
-    ):
-        return {"next_url": request.get_full_path()}
-    return {}
+    """Return a function by the same name as the context processor.
+    That means, in the jinja templates, instead of doing
+
+        {% set url = next_url %}
+
+    you just have to do:
+
+        {% set url = next_url() %}
+
+    which means that the actual context processor function isn't executed
+    every single time any jinja template is rendered. Now, only if the
+    context processor is actually needed, it gets executed.
+
+    See https://www.peterbe.com/plog/closure-django-context-processors
+    """
+
+    def inner():
+        if hasattr(request, "path"):
+            if request.GET.get("next"):
+                if "://" not in request.GET["next"]:
+                    return request.GET["next"]
+            elif reverse(settings.LOGIN_URL) != request.get_full_path():
+                # The only exception is the sign-in landing page which you get to
+                # if you can't use the auth modal.
+                return request.get_full_path()
+
+    return {"next_url": inner}


Test patch:
diff --git a/kuma/core/tests/test_misc.py b/kuma/core/tests/test_misc.py
--- a/kuma/core/tests/test_misc.py
+++ b/kuma/core/tests/test_misc.py
@@ -1,23 +1,34 @@
 from django.test import RequestFactory
 
-from . import KumaTestCase
-from ..context_processors import next_url
+from kuma.core.context_processors import next_url
+from kuma.core.urlresolvers import reverse
 
 
-class TestNextUrl(KumaTestCase):
-    """
-    Tests that the next_url value is properly set,
-    including query string
-    """
+def test_next_url_basic():
+    path = "/one/two"
+    request = RequestFactory().get(path)
+    assert path == next_url(request)["next_url"]()
 
-    rf = RequestFactory()
 
-    def test_basic(self):
-        path = "/one/two"
-        request = self.rf.get(path)
-        assert path == next_url(request)["next_url"]
+def test_next_url_querystring():
+    path = "/one/two?something"
+    request = RequestFactory().get(path)
+    assert path == next_url(request)["next_url"]()
 
-    def test_querystring(self):
-        path = "/one/two?something"
-        request = self.rf.get(path)
-        assert path == next_url(request)["next_url"]
+
+def test_next_url_with_next_querystring():
+    path = "/one/two?next=/foo/bar"
+    request = RequestFactory().get(path)
+    assert next_url(request)["next_url"]() == "/foo/bar"
+
+
+def test_next_url_with_next_querystring_but_remote():
+    path = "/one/two?next=http://foo/bar"
+    request = RequestFactory().get(path)
+    assert next_url(request)["next_url"]() is None
+
+
+def test_next_url_already_on_login_url(settings):
+    path = reverse(settings.LOGIN_URL)
+    request = RequestFactory().get(path)
+    assert next_url(request)["next_url"]() is None
diff --git a/kuma/users/tests/test_views.py b/kuma/users/tests/test_views.py
--- a/kuma/users/tests/test_views.py
+++ b/kuma/users/tests/test_views.py
@@ -2,6 +2,7 @@
 from datetime import timedelta
 from textwrap import dedent
 from unittest import mock
+from urllib.parse import urlencode
 
 import pytest
 import requests_mock
@@ -1908,7 +1909,7 @@ def test_invalid_uid_fails(wiki_user, client):
 
 
 def test_signin_landing(db, client, settings):
-    response = client.get(reverse("socialaccount_signin"))
+    response = client.get(reverse(settings.LOGIN_URL))
     github_login_url = "/users/github/login/"
     google_login_url = "/users/google/login/"
     # first, make sure that the page loads
@@ -1919,3 +1920,34 @@ def test_signin_landing(db, client, settings):
     # ensure each button links to the appropriate endpoint
     assert doc(".github-auth").attr.href == github_login_url
     assert doc(".google-auth").attr.href == google_login_url
+    # just to be absolutely clear, there is no ?next=... on *this* page
+    assert "next" not in doc(".github-auth").attr.href
+    assert "next" not in doc(".google-auth").attr.href
+
+
+def test_signin_landing_next(db, client, settings):
+    """Going to /en-US/users/account/signup-landing?next=THIS should pick put
+    that 'THIS' and put it into the Google and GitHub auth links."""
+    next_page = "/en-US/Foo/Bar"
+    response = client.get(reverse(settings.LOGIN_URL), {"next": next_page})
+    assert response.status_code == 200
+    doc = pq(response.content)
+    github_login_url = "/users/github/login/"
+    google_login_url = "/users/google/login/"
+    next = f"?{urlencode({'next': next_page})}"
+    assert doc(".github-auth").attr.href == github_login_url + next
+    assert doc(".google-auth").attr.href == google_login_url + next
+
+
+def test_next_paramter_in_auth_links(db, client, root_doc):
+    """View any other page and observe that the auth links (they're part of the
+    auth modal) have a ?next= link on them."""
+    url = root_doc.get_absolute_url()
+    response = client.get(url)
+    assert response.status_code == 200
+    doc = pq(response.content)
+    github_login_url = "/users/github/login/"
+    google_login_url = "/users/google/login/"
+    next = f"?{urlencode({'next': url})}"
+    assert doc(".github-auth").attr.href == github_login_url + next
+    assert doc(".google-auth").attr.href == google_login_url + next