diff --git a/src/pretalx/common/templatetags/oauth_tags.py b/src/pretalx/common/templatetags/oauth_tags.py
new file mode 100644
index 000000000..d3f1fd543
--- /dev/null
+++ b/src/pretalx/common/templatetags/oauth_tags.py
@@ -0,0 +1,31 @@
+from typing import Optional
+from urllib.parse import quote
+
+from django import template
+from django.urls import reverse
+
+register = template.Library()
+
+
+@register.simple_tag
+def oauth_login_url(next_url: Optional[str] = None) -> str:
+ """
+ Generate the OAuth login URL with an optional next parameter.
+ Usage: {% oauth_login_url next_url %}
+ """
+ base_url = reverse("eventyay_common:oauth2_provider.login")
+ if next_url:
+ return f"{base_url}?next={quote(next_url)}"
+ return base_url
+
+
+@register.simple_tag
+def register_account_url(next_url: Optional[str] = None) -> str:
+ """
+ Generate the registration URL with an optional next parameter.
+ Usage: {% register_account_url next_url %}
+ """
+ base_url = reverse("eventyay_common:register.account")
+ if next_url:
+ return f"{base_url}?next={quote(next_url)}"
+ return base_url
diff --git a/src/pretalx/eventyay_common/urls.py b/src/pretalx/eventyay_common/urls.py
index 88cbe049e..b6025fbdb 100644
--- a/src/pretalx/eventyay_common/urls.py
+++ b/src/pretalx/eventyay_common/urls.py
@@ -11,7 +11,8 @@
urlpatterns = [
path("oauth2/", include("oauth2_provider.urls", namespace="oauth2_provider")),
- path("login/", auth.oauth2_login_view, name="oauth2_provider.login"),
+ path("login/", auth.OAuth2LoginView.as_view(), name="oauth2_provider.login"),
+ path("register/", auth.register, name="register.account"),
path("oauth2/callback/", auth.oauth2_callback, name="oauth2_callback"),
path("webhook/organiser/", organiser_webhook, name="webhook.organiser"),
path("webhook/team/", team_webhook, name="webhook.team"),
diff --git a/src/pretalx/eventyay_common/views/auth.py b/src/pretalx/eventyay_common/views/auth.py
index 8e0d351d5..e6215f386 100644
--- a/src/pretalx/eventyay_common/views/auth.py
+++ b/src/pretalx/eventyay_common/views/auth.py
@@ -1,12 +1,16 @@
import logging
import os
+from typing import Optional, Tuple
+from urllib.parse import quote, urljoin, urlparse
from allauth.socialaccount.models import SocialApp
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import login
+from django.http import HttpRequest, HttpResponse
from django.shortcuts import redirect
from django.urls import reverse
+from django.views import View
from requests_oauthlib import OAuth2Session
from pretalx.person.models import User
@@ -20,34 +24,77 @@
)
-def oauth2_login_view(request, *args, **kwargs):
- sso_provider = SocialApp.objects.filter(
- provider=settings.EVENTYAY_SSO_PROVIDER
- ).first()
- if not sso_provider:
+def validate_relative_url(next_url: str) -> bool:
+ """
+ Only allow relative urls
+ """
+ parsed = urlparse(next_url)
+ if parsed.scheme or parsed.netloc:
+ return False
+
+ return True
+
+
+def register(request: HttpRequest) -> HttpResponse:
+ """
+ Register a new user account and redirect to the previous page.
+
+ This function constructs a registration URL with a 'next' parameter
+ to ensure the user is redirected back to their original location
+ after registration.
+ """
+ register_url = urljoin(settings.EVENTYAY_TICKET_BASE_PATH, "/control/register")
+ next_url = request.GET.get("next") or request.POST.get("next")
+ if next_url and validate_relative_url(next_url):
+ full_next_url = request.build_absolute_uri(next_url)
+ next_param = f"?next={quote(full_next_url)}"
+ return redirect(f"{register_url}{next_param}")
+
+ return redirect(register_url)
+
+
+class OAuth2LoginView(View):
+ def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
+ # Store the 'next' URL in the session, for redirecting user back after login
+ next_url = request.GET.get("next") or request.POST.get("next")
+ if next_url and validate_relative_url(next_url):
+ request.session["next"] = next_url
+
+ sso_provider = self.get_sso_provider()
+ if not sso_provider:
+ return self.handle_sso_not_configured(request)
+ oauth2_session = self.create_oauth2_session(sso_provider)
+ authorization_url, state = self.get_authorization_url(oauth2_session)
+ request.session["oauth2_state"] = state
+
+ return redirect(authorization_url)
+
+ @staticmethod
+ def get_sso_provider() -> Optional[SocialApp]:
+ return SocialApp.objects.filter(provider=settings.EVENTYAY_SSO_PROVIDER).first()
+
+ @staticmethod
+ def handle_sso_not_configured(request: HttpRequest) -> HttpResponse:
messages.error(
request,
"SSO not configured yet, please contact the "
"administrator or come back later.",
)
return redirect(reverse("orga:login"))
- # Create an OAuth2 session using the client ID and redirect URI
- oauth2_session = OAuth2Session(
- client_id=sso_provider.client_id,
- redirect_uri=settings.OAUTH2_PROVIDER["REDIRECT_URI"],
- scope=settings.OAUTH2_PROVIDER["SCOPE"],
- )
- # Generate the authorization URL for the SSO provider
- authorization_url, state = oauth2_session.authorization_url(
- settings.OAUTH2_PROVIDER["AUTHORIZE_URL"]
- )
-
- # Save the OAuth2 session state to the user's session for security
- request.session["oauth2_state"] = state
+ @staticmethod
+ def create_oauth2_session(sso_provider: SocialApp) -> OAuth2Session:
+ return OAuth2Session(
+ client_id=sso_provider.client_id,
+ redirect_uri=settings.OAUTH2_PROVIDER["REDIRECT_URI"],
+ scope=settings.OAUTH2_PROVIDER["SCOPE"],
+ )
- # Redirect to the SSO provider's login page
- return redirect(authorization_url)
+ @staticmethod
+ def get_authorization_url(oauth2_session: OAuth2Session) -> Tuple[str, str]:
+ return oauth2_session.authorization_url(
+ settings.OAUTH2_PROVIDER["AUTHORIZE_URL"]
+ )
def oauth2_callback(request):
@@ -98,4 +145,8 @@ def oauth2_callback(request):
# Log the user into the session
login(request, user, backend="django.contrib.auth.backends.ModelBackend")
+ # If a 'next' URL was stored in the session, use it for redirecting user back after login
+ next_url = request.session.pop("next", None)
+ if next_url and validate_relative_url(next_url):
+ return redirect(next_url)
return redirect(reverse("cfp:root.main"))
diff --git a/src/pretalx/orga/templates/orga/auth/login.html b/src/pretalx/orga/templates/orga/auth/login.html
index 9fff8bd71..792d402d3 100644
--- a/src/pretalx/orga/templates/orga/auth/login.html
+++ b/src/pretalx/orga/templates/orga/auth/login.html
@@ -3,5 +3,5 @@
{% load static %}
{% block title %}{% translate "Sign in" %}{% endblock title %}
{% block content %}
- {% include "common/auth.html" with hide_register=True %}
+ {% include "common/auth.html" with hide_register=True next_url=request.get_full_path %}
{% endblock content %}
diff --git a/src/pretalx/orga/templates/orga/invitation.html b/src/pretalx/orga/templates/orga/invitation.html
index 190249ae6..3cf972e45 100644
--- a/src/pretalx/orga/templates/orga/invitation.html
+++ b/src/pretalx/orga/templates/orga/invitation.html
@@ -24,7 +24,7 @@
- {% include "common/auth.html" %}
+ {% include "common/auth.html" with next_url=request.get_full_path %}
{% else %}