Repository: readthedocs/readthedocs.org

Issue:
Allauth: update to use latest version
## What's the problem this feature will solve?

The latest versions of allauth include some improvements and fixes related to SAML, which is a feature we are active developing for .com.

Last time I saw that were some breaking changes related to templates, and changes around using json fields.

## Additional context

The last time we updated allauth (https://github.com/readthedocs/readthedocs.org/pull/11134) we hit only one problem I think, it was related to a missing changelog entry about a field change in google. Another problem we had we about the settings, but that wasn't related to allauth itself, but to our settings inheritance.


Discussion:
> latest versions of allauth include some improvements and fixes related to SAML

What are those improvements and fixes? Knowing that will help us to work on prioritization of this upgrade.
You can just check the changelog and look for "SAML" https://docs.allauth.org/en/latest/release-notes/recent.html Some important ones are:

- SAML: accessing the SLS/ACS views using a GET request would result in a crash (500).
- SAML: the login view did not obey the SOCIALACCOUNT_LOGIN_ON_GET = False setting.

Resolving patch:
diff --git a/readthedocs/oauth/services/gitlab.py b/readthedocs/oauth/services/gitlab.py
--- a/readthedocs/oauth/services/gitlab.py
+++ b/readthedocs/oauth/services/gitlab.py
@@ -35,7 +35,7 @@ class GitLabService(Service):
     # Just use the network location to determine if it's a GitLab project
     # because private repos have another base url, eg. git@gitlab.example.com
     url_pattern = re.compile(
-        re.escape(urlparse(adapter.provider_base_url).netloc),
+        re.escape(urlparse(adapter.provider_default_url).netloc),
     )
 
     PERMISSION_NO_ACCESS = 0
@@ -73,7 +73,7 @@ def sync_repositories(self):
         remote_repositories = []
         try:
             repos = self.paginate(
-                "{url}/api/v4/projects".format(url=self.adapter.provider_base_url),
+                "{url}/api/v4/projects".format(url=self.adapter.provider_default_url),
                 per_page=100,
                 archived=False,
                 order_by="path",
@@ -100,7 +100,7 @@ def sync_organizations(self):
 
         try:
             orgs = self.paginate(
-                "{url}/api/v4/groups".format(url=self.adapter.provider_base_url),
+                "{url}/api/v4/groups".format(url=self.adapter.provider_default_url),
                 per_page=100,
                 all_available=False,
                 order_by="path",
@@ -110,7 +110,7 @@ def sync_organizations(self):
                 remote_organization = self.create_organization(org)
                 org_repos = self.paginate(
                     "{url}/api/v4/groups/{id}/projects".format(
-                        url=self.adapter.provider_base_url,
+                        url=self.adapter.provider_default_url,
                         id=org["id"],
                     ),
                     per_page=100,
@@ -131,7 +131,7 @@ def sync_organizations(self):
                         # which contains the admin permission fields.
                         resp = self.get_session().get(
                             "{url}/api/v4/projects/{id}".format(
-                                url=self.adapter.provider_base_url, id=repo["id"]
+                                url=self.adapter.provider_default_url, id=repo["id"]
                             )
                         )
 
@@ -270,7 +270,7 @@ def create_organization(self, fields):
         organization.name = fields.get("name")
         organization.slug = fields.get("path")
         organization.url = "{url}/{path}".format(
-            url=self.adapter.provider_base_url,
+            url=self.adapter.provider_default_url,
             path=fields.get("path"),
         )
         organization.avatar_url = fields.get("avatar_url")
@@ -336,7 +336,7 @@ def get_provider_data(self, project, integration):
         try:
             resp = session.get(
                 "{url}/api/v4/projects/{repo_id}/hooks".format(
-                    url=self.adapter.provider_base_url,
+                    url=self.adapter.provider_default_url,
                     repo_id=repo_id,
                 ),
             )
@@ -383,7 +383,7 @@ def setup_webhook(self, project, integration=None):
             )
 
         repo_id = self._get_repo_id(project)
-        url = f"{self.adapter.provider_base_url}/api/v4/projects/{repo_id}/hooks"
+        url = f"{self.adapter.provider_default_url}/api/v4/projects/{repo_id}/hooks"
 
         if repo_id is None:
             return (False, resp)
@@ -462,7 +462,7 @@ def update_webhook(self, project, integration):
             hook_id = provider_data.get("id")
             resp = session.put(
                 "{url}/api/v4/projects/{repo_id}/hooks/{hook_id}".format(
-                    url=self.adapter.provider_base_url,
+                    url=self.adapter.provider_default_url,
                     repo_id=repo_id,
                     hook_id=hook_id,
                 ),
@@ -537,7 +537,7 @@ def send_build_status(self, build, commit, status):
             "description": description,
             "context": context,
         }
-        url = f"{self.adapter.provider_base_url}/api/v4/projects/{repo_id}/statuses/{commit}"
+        url = f"{self.adapter.provider_default_url}/api/v4/projects/{repo_id}/statuses/{commit}"
 
         log.bind(
             project_slug=project.slug,
diff --git a/readthedocs/settings/base.py b/readthedocs/settings/base.py
--- a/readthedocs/settings/base.py
+++ b/readthedocs/settings/base.py
@@ -285,7 +285,6 @@ def INSTALLED_APPS(self):  # noqa
             "allauth.socialaccount",
             "allauth.socialaccount.providers.github",
             "allauth.socialaccount.providers.gitlab",
-            "allauth.socialaccount.providers.bitbucket",
             "allauth.socialaccount.providers.bitbucket_oauth2",
             "cacheops",
         ]
@@ -698,12 +697,6 @@ def DOCKER_LIMITS(self):
             ],
             # Bitbucket scope/permissions are determined by the Oauth consumer setup on bitbucket.org.
         },
-        # Deprecated, we use `bitbucket_oauth2` for all new connections.
-        "bitbucket": {
-            "APPS": [
-                {"client_id": "123", "secret": "456", "key": ""},
-            ],
-        },
     }
 
     @property


Test patch:
diff --git a/readthedocs/rtd_tests/tests/test_celery.py b/readthedocs/rtd_tests/tests/test_celery.py
--- a/readthedocs/rtd_tests/tests/test_celery.py
+++ b/readthedocs/rtd_tests/tests/test_celery.py
@@ -149,7 +149,7 @@ def test_send_build_status_no_remote_repo_or_social_account_github(
             notification.format_values,
             {
                 "provider_name": "GitHub",
-                "url_connect_account": "/accounts/social/connections/",
+                "url_connect_account": "/accounts/3rdparty/",
             },
         )
 
@@ -222,6 +222,6 @@ def test_send_build_status_no_remote_repo_or_social_account_gitlab(
             notification.format_values,
             {
                 "provider_name": "GitLab",
-                "url_connect_account": "/accounts/social/connections/",
+                "url_connect_account": "/accounts/3rdparty/",
             },
         )
diff --git a/requirements/testing.in b/requirements/testing.in
--- a/requirements/testing.in
+++ b/requirements/testing.in
@@ -2,7 +2,7 @@
 
 -r pip.txt
 
-sphinx
+sphinx[test]
 django-dynamic-fixture
 pytest
 pytest-custom-exit-code
diff --git a/requirements/testing.txt b/requirements/testing.txt
--- a/requirements/testing.txt
+++ b/requirements/testing.txt
@@ -91,10 +91,10 @@ cssselect==1.2.0
     # via
     #   -r requirements/pip.txt
     #   pyquery
+cython==3.0.10
+    # via sphinx
 defusedxml==0.7.1
-    # via
-    #   -r requirements/pip.txt
-    #   python3-openid
+    # via sphinx
 distlib==0.3.8
     # via
     #   -r requirements/pip.txt
@@ -125,7 +125,7 @@ django==4.2.13
     #   django-timezone-field
     #   djangorestframework
     #   jsonfield
-django-allauth[saml]==0.57.2
+django-allauth[saml,socialaccount]==0.63.1
     # via -r requirements/pip.txt
 django-annoying==0.10.6
     # via -r requirements/pip.txt
@@ -317,6 +317,7 @@ pytest==8.2.0
     #   pytest-custom-exit-code
     #   pytest-django
     #   pytest-mock
+    #   sphinx
 pytest-cov==5.0.0
     # via -r requirements/testing.in
 pytest-custom-exit-code==0.3.0
@@ -335,10 +336,6 @@ python-dateutil==2.9.0.post0
     #   botocore
     #   elasticsearch-dsl
     #   python-crontab
-python3-openid==3.2.0
-    # via
-    #   -r requirements/pip.txt
-    #   django-allauth
 python3-saml==1.16.0
     # via
     #   -r requirements/pip.txt
@@ -396,7 +393,7 @@ slumber==0.7.1
     # via -r requirements/pip.txt
 snowballstemmer==2.2.0
     # via sphinx
-sphinx==7.3.7
+sphinx[test]==7.3.7
     # via -r requirements/testing.in
 sphinxcontrib-applehelp==1.0.8
     # via sphinx
@@ -483,3 +480,6 @@ xmlsec==1.3.13
     #   python3-saml
 yamale==2.2.0
     # via -r requirements/testing.in
+
+# The following packages are considered to be unsafe in a requirements file:
+# setuptools