From 21cb467edcf298289126360ad19fb9599fb20d90 Mon Sep 17 00:00:00 2001
From: Jonathan Weth <git@jonathanweth.de>
Date: Sat, 8 Jan 2022 15:47:04 +0100
Subject: [PATCH] Visually indicate the OpenID login process by improved design

---
 aleksis/core/static/public/style.scss         | 12 +++++
 .../templates/oauth2_provider/authorize.html  | 11 ++++-
 .../core/templates/two_factor/core/login.html | 33 +++++++++++---
 aleksis/core/urls.py                          |  5 +++
 aleksis/core/views.py                         | 45 ++++++++++++++++++-
 5 files changed, 96 insertions(+), 10 deletions(-)

diff --git a/aleksis/core/static/public/style.scss b/aleksis/core/static/public/style.scss
index 09ecb9c1a..4fb6d9b28 100644
--- a/aleksis/core/static/public/style.scss
+++ b/aleksis/core/static/public/style.scss
@@ -804,3 +804,15 @@ main figure.alert {
   height: 24px;
   margin-bottom: 8px;
 }
+
+.application-circle {
+  border-radius: 50%;
+  width: 20vh;
+  height: 20vh;
+}
+
+
+.application-circle img {
+  @extend .application-circle;
+    object-fit: cover;
+}
diff --git a/aleksis/core/templates/oauth2_provider/authorize.html b/aleksis/core/templates/oauth2_provider/authorize.html
index 48b996837..c90d5e8dd 100644
--- a/aleksis/core/templates/oauth2_provider/authorize.html
+++ b/aleksis/core/templates/oauth2_provider/authorize.html
@@ -12,8 +12,15 @@
     <div class="col s12 m10 l8 xl6">
       <div class="card">
         <div class="card-content">
-          <div class="card-title">
-            {% trans "Authorize" %} {{ application.name }}
+          {% if application.icon %}
+            <div class="center-via-flex margin-bottom">
+              <div class="application-circle materialboxed z-depth-2">
+                <img src="{{ application.icon.url }}" alt="{{ application.name }}" class="hundred-percent">
+              </div>
+            </div>
+          {% endif %}
+          <div class="card-title {% if application.icon %}center{% endif %}">
+            {% blocktrans with name=application.name %}Authorize {{ name }}{% endblocktrans %}
           </div>
           <p class="margin-bottom">{% trans "The application requests access to the following scopes:" %}</p>
           {% for scope in scopes_descriptions %}
diff --git a/aleksis/core/templates/two_factor/core/login.html b/aleksis/core/templates/two_factor/core/login.html
index 9ea4bc6c2..834c4b98b 100644
--- a/aleksis/core/templates/two_factor/core/login.html
+++ b/aleksis/core/templates/two_factor/core/login.html
@@ -16,7 +16,17 @@
       <div class="col s12 m10 l8 xl6">
         <div class="card">
           <div class="card-content">
-            {% if wizard.steps.current == 'auth' and socialaccount_providers %}
+            {% if oauth and oauth_application.icon %}
+              <div class="center-via-flex margin-bottom">
+                <div class="application-circle materialboxed z-depth-2">
+                  <img src="{{ oauth_application.icon.url }}" alt="{{ oauth_application.name }}"
+                       class="hundred-percent">
+                </div>
+              </div>
+              <div class="card-title center">
+                {% blocktrans with name=oauth_application.name %}Login for {{ name }}{% endblocktrans %}
+              </div>
+            {% elif wizard.steps.current == 'auth' and socialaccount_providers %}
               <div class="card-title">{% trans "Login with username and password" %}</div>
             {% else %}
               <div class="card-title">{% trans "Login" %}</div>
@@ -30,12 +40,21 @@
                 </p>
               </div>
             {% elif wizard.steps.current == 'auth' %}
-              <div class="alert primary">
-                <p>
-                  <i class="material-icons left">info</i>
-                  {% blocktrans %}Please login to see this page.{% endblocktrans %}
-                </p>
-              </div>
+              {% if oauth %}
+                <div class="alert primary">
+                  <p>
+                    <i class="material-icons left">info</i>
+                    {% blocktrans %}Please login with your account to use the external application.{% endblocktrans %}
+                  </p>
+                </div>
+              {% else %}
+                <div class="alert primary">
+                  <p>
+                    <i class="material-icons left">info</i>
+                    {% blocktrans %}Please login to see this page.{% endblocktrans %}
+                  </p>
+                </div>
+              {% endif %}
             {% endif %}
             {% if not wizard.steps.current == "auth" %}
               <div class="alert primary">
diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py
index 753d95085..b5dcb004d 100644
--- a/aleksis/core/urls.py
+++ b/aleksis/core/urls.py
@@ -134,6 +134,11 @@ urlpatterns = [
         views.OAuth2EditView.as_view(),
         name="edit_oauth2_application",
     ),
+    path(
+        "oauth/authorize/",
+        views.CustomAuthorizationView.as_view(),
+        name="oauth2_provider:authorize",
+    ),
     path("oauth/", include("oauth2_provider.urls", namespace="oauth2_provider")),
     path("__i18n__/", include("django.conf.urls.i18n")),
     path(
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index cfb9cd5b3..6354751aa 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -1,6 +1,6 @@
 from textwrap import wrap
 from typing import Any, Dict, Optional, Type
-from urllib.parse import urlencode
+from urllib.parse import urlencode, urlparse, urlunparse
 
 from django.apps import apps
 from django.conf import settings
@@ -18,6 +18,7 @@ from django.http import (
     HttpResponseRedirect,
     HttpResponseServerError,
     JsonResponse,
+    QueryDict,
 )
 from django.shortcuts import get_object_or_404, redirect, render
 from django.template import loader
@@ -50,6 +51,9 @@ from haystack.query import SearchQuerySet
 from haystack.utils.loading import UnifiedIndex
 from health_check.views import MainView
 from invitations.views import SendInvite, accept_invitation
+from oauth2_provider.exceptions import OAuthToolkitError
+from oauth2_provider.models import get_application_model
+from oauth2_provider.views import AuthorizationView
 from reversion import set_user
 from reversion.views import RevisionMixin
 from rules import test_rule
@@ -1460,3 +1464,42 @@ class LoginView(AllAuthLoginView):
                 return render(self.request, "account/verification_sent.html")
 
         return super().done(form_list, **kwargs)
+
+    def get_context_data(self, form, **kwargs):
+        """Override context data to hide side menu and include OAuth2 application if given."""
+        context = super().get_context_data(form, **kwargs)
+        if self.request.GET.get("oauth"):
+            context["no_menu"] = True
+
+            if self.request.GET.get("client_id"):
+                application = get_application_model().objects.get(
+                    client_id=self.request.GET["client_id"]
+                )
+                context["oauth_application"] = application
+        return context
+
+
+class CustomAuthorizationView(AuthorizationView):
+    def handle_no_permission(self):
+        """Override handle_no_permission to provide OAuth2 information to login page."""
+        redirect_obj = super().handle_no_permission()
+
+        try:
+            scopes, credentials = self.validate_authorization_request(self.request)
+        except OAuthToolkitError as error:
+            # Application is not available at this time.
+            return self.error_response(error, application=None)
+
+        login_url_parts = list(urlparse(redirect_obj.url))
+        querystring = QueryDict(login_url_parts[4], mutable=True)
+        querystring["oauth"] = "yes"
+        querystring["client_id"] = credentials["client_id"]
+        login_url_parts[4] = querystring.urlencode(safe="/")
+
+        return HttpResponseRedirect(urlunparse(login_url_parts))
+
+    def get_context_data(self, **kwargs):
+        """Override context data to hide side menu."""
+        context = super().get_context_data(**kwargs)
+        context["no_menu"] = True
+        return context
-- 
GitLab