diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d009032fe7601c866668a032b1043e0dd18c2418..e33418c1cdaa57681a0bfe868902c833b7b72683 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -19,6 +19,8 @@ Added
 
 * OpenID Connect RSA keys can now be passed as string in config files
 * Views filtering for person names now also search the username of a linked user
+* OAuth2 applications now take an icon which is shown in the authorization progress.
+* Add support for hiding the main side nav in ``base.html``.
 
 Fixed
 ~~~~~
@@ -41,6 +43,8 @@ Changed
 * [Docker] Base image now contains curl, grep, less, sed, and pspg
 * Views raising a 404 error can now customise the message that is displayed on the error page
 * OpenID Connect is enabled by default now, without RSA support
+* Login and authorization pages for OAuth2/OpenID Connect now indicate that the user is in progress
+  to authorize an external application.
 
 `2.5`_ – 2022-01-02
 -------------------
diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py
index eb2465870a36174997c73a967b04690b30d60051..4bd7e1b94419420d969b5690f31a4a7c89b85ae2 100644
--- a/aleksis/core/forms.py
+++ b/aleksis/core/forms.py
@@ -775,6 +775,7 @@ class OAuthApplicationForm(forms.ModelForm):
         model = OAuthApplication
         fields = (
             "name",
+            "icon",
             "client_id",
             "client_secret",
             "client_type",
diff --git a/aleksis/core/migrations/0031_oauthapplication_icon.py b/aleksis/core/migrations/0031_oauthapplication_icon.py
new file mode 100644
index 0000000000000000000000000000000000000000..26f2d6575e768578e653088c582fd3dadd147dc5
--- /dev/null
+++ b/aleksis/core/migrations/0031_oauthapplication_icon.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.11 on 2022-01-08 13:59
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0030_user_attributes'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='oauthapplication',
+            name='icon',
+            field=models.ImageField(blank=True, help_text='This image will be shown as icon in the authorization flow. It should be squared.', null=True, upload_to='', verbose_name='Icon'),
+        ),
+    ]
diff --git a/aleksis/core/models.py b/aleksis/core/models.py
index 133af202290421dd215b568cd6a1133adc239571..91b45a1df196edb8cb777a3ea61a84fa984fca3d 100644
--- a/aleksis/core/models.py
+++ b/aleksis/core/models.py
@@ -1248,6 +1248,15 @@ class OAuthApplication(AbstractApplication):
         blank=True,
     )
 
+    icon = models.ImageField(
+        verbose_name=_("Icon"),
+        blank=True,
+        null=True,
+        help_text=_(
+            "This image will be shown as icon in the authorization flow. It should be squared."
+        ),
+    )
+
     def allows_grant_type(self, *grant_types: set[str]) -> bool:
         allowed_grants = get_site_preferences()["auth__oauth_allowed_grants"]
 
diff --git a/aleksis/core/static/public/style.scss b/aleksis/core/static/public/style.scss
index 618b846dc956af8ed62db357f2867a9988b58846..4fb6d9b2815555082d18cfd7ef18025704010035 100644
--- a/aleksis/core/static/public/style.scss
+++ b/aleksis/core/static/public/style.scss
@@ -64,6 +64,10 @@ header, main, footer {
   margin-left: 300px;
 }
 
+.without-menu header, .without-menu main, .without-menu footer {
+  margin-left: 0;
+}
+
 @media only screen and (max-width: 992px) {
   header, main, footer {
     margin-left: 0;
@@ -800,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/core/base.html b/aleksis/core/templates/core/base.html
index 06aebc4533369e9b7af39051dcb80713193f50d5..7fd4e38f8449b0a649fcd01e08051a2332070a15 100644
--- a/aleksis/core/templates/core/base.html
+++ b/aleksis/core/templates/core/base.html
@@ -58,7 +58,7 @@
 
   {% block extra_head %}{% endblock %}
 </head>
-<body>
+<body {% if no_menu %}class="without-menu"{% endif %}>
 
 <header>
   <!-- Menu button (sidenav) -->
@@ -88,32 +88,36 @@
   </nav>
 
   <!-- Main nav (sidenav) -->
-  <ul id="slide-out" class="sidenav sidenav-fixed">
-    <li class="logo">
-      {% static "img/aleksis-banner.svg" as aleksis_banner %}
-      <a id="logo-container" href="/" class="brand-logo">
-        <img src="{% firstof request.site.preferences.theme__logo.url aleksis_banner %}"
-             alt="{{ request.site.preferences.general__title }} – Logo">
-      </a>
-    </li>
-    {% has_perm 'core.search_rule' user as search %}
-    {% if search %}
-      <li class="search">
-        <form method="get" action="{% url "haystack_search" %}" id="search-form" class="autocomplete">
-          <div class="search-wrapper">
-            <input id="search" name="q" type="search" enterkeyhint="search" placeholder="{% trans "Search" %}">
-            <button class="btn btn-flat search-button" type="submit" aria-label="{% trans "Search" %}">
-              <i class="material-icons">search</i>
-            </button>
-            <div class="progress" id="search-loader"><div class="indeterminate"></div></div>
-          </div>
-        </form>
+  {% if not no_menu %}
+    <ul id="slide-out" class="sidenav sidenav-fixed">
+      <li class="logo">
+        {% static "img/aleksis-banner.svg" as aleksis_banner %}
+        <a id="logo-container" href="/" class="brand-logo">
+          <img src="{% firstof request.site.preferences.theme__logo.url aleksis_banner %}"
+               alt="{{ request.site.preferences.general__title }} – Logo">
+        </a>
       </li>
-    {% endif %}
-    <li class="no-padding">
-      {% include "core/partials/sidenav.html" %}
-    </li>
-  </ul>
+      {% has_perm 'core.search_rule' user as search %}
+      {% if search %}
+        <li class="search">
+          <form method="get" action="{% url "haystack_search" %}" id="search-form" class="autocomplete">
+            <div class="search-wrapper">
+              <input id="search" name="q" type="search" enterkeyhint="search" placeholder="{% trans "Search" %}">
+              <button class="btn btn-flat search-button" type="submit" aria-label="{% trans "Search" %}">
+                <i class="material-icons">search</i>
+              </button>
+              <div class="progress" id="search-loader">
+                <div class="indeterminate"></div>
+              </div>
+            </div>
+          </form>
+        </li>
+      {% endif %}
+      <li class="no-padding">
+        {% include "core/partials/sidenav.html" %}
+      </li>
+    </ul>
+  {% endif %}
 </header>
 
 
diff --git a/aleksis/core/templates/oauth2_provider/application/create.html b/aleksis/core/templates/oauth2_provider/application/create.html
index d81489e922a76de8d7a8e92a7f48686714a3b3a7..73b94677206d38fca163858551536d575b2dce3d 100644
--- a/aleksis/core/templates/oauth2_provider/application/create.html
+++ b/aleksis/core/templates/oauth2_provider/application/create.html
@@ -6,7 +6,7 @@
 {% block page_title %}{% blocktrans %}Register OAuth2 Application{% endblocktrans %}{% endblock %}
 
 {% block content %}
-  <form method="post">
+  <form method="post" enctype="multipart/form-data">
     {% csrf_token %}
     {% form form=form %}{% endform %}
     {% include "core/partials/save_button.html" %}
diff --git a/aleksis/core/templates/oauth2_provider/application/detail.html b/aleksis/core/templates/oauth2_provider/application/detail.html
index da6b8abf81ddc1916de7b0c7feb5a7262f453037..28e2af7d70ff4b6dd5d93b46b8ec52d31bc36538 100644
--- a/aleksis/core/templates/oauth2_provider/application/detail.html
+++ b/aleksis/core/templates/oauth2_provider/application/detail.html
@@ -22,6 +22,18 @@
   </a>
   <table class="responsive-table">
     <tbody>
+    <tr>
+      <th>{% trans "Icon" %}</th>
+      <td>
+        {% if application.icon %}
+          <div class="application-circle materialboxed z-depth-2">
+            <img src="{{ application.icon.url }}" alt="{{ oauth_application.name }}" class="hundred-percent">
+          </div>
+        {% else %}
+          –
+        {% endif %}
+      </td>
+    </tr>
     <tr>
       <th>
         {% trans "Client id" %}
diff --git a/aleksis/core/templates/oauth2_provider/application/edit.html b/aleksis/core/templates/oauth2_provider/application/edit.html
index 6755d2420fb6a181f671b825475afb4ec9581521..30f50fff94e330e941d7b4730fe7d875b039d74e 100644
--- a/aleksis/core/templates/oauth2_provider/application/edit.html
+++ b/aleksis/core/templates/oauth2_provider/application/edit.html
@@ -6,7 +6,7 @@
 {% block page_title %}{% blocktrans %}Edit OAuth2 Application{% endblocktrans %}{% endblock %}
 
 {% block content %}
-  <form method="post">
+  <form method="post" enctype="multipart/form-data">
     {% csrf_token %}
     {% form form=form %}{% endform %}
     {% include "core/partials/save_button.html" %}
diff --git a/aleksis/core/templates/oauth2_provider/application/list.html b/aleksis/core/templates/oauth2_provider/application/list.html
index 06f1a95c4e05c14dcb5efe69f2416040d184f0bb..ced7d718dfe1b561c2ea8253e25658a9f449024d 100644
--- a/aleksis/core/templates/oauth2_provider/application/list.html
+++ b/aleksis/core/templates/oauth2_provider/application/list.html
@@ -12,8 +12,13 @@
   </a>
   <div class="collection">
     {% for application in applications %}
-      <a class="collection-item" href="{% url "oauth2_application" application.id %}">
-        {{ application.name }}
+      <a class="collection-item avatar" href="{% url "oauth2_application" application.id %}">
+        {% if application.icon %}
+          <img src="{{ application.icon.url }}" alt="{{ application.name }}" class="circle">
+        {% endif %}
+        <span class="title">
+          {{ application.name }}
+        </span>
       </a>
       {% empty %}
       <div class="collection-item flow-text">
diff --git a/aleksis/core/templates/oauth2_provider/authorize.html b/aleksis/core/templates/oauth2_provider/authorize.html
index 48b996837b8721ef6ba74337d286236e91729f5b..c90d5e8dd9d3d72cff9e19ce167487900e904636 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 9ea4bc6c24b7d9e764742bbddf75ceb86b785868..834c4b98b543bd994542b105aec1b99932f7d186 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 753d95085b6c8812927f2db376a6f3e2e6429c4b..b5dcb004d040b64b22242b977702a104fb2ea33a 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 cfb9cd5b3050af79341958aa60f72ad88d097923..6354751aa93e0d75326036ff165a02c4174dee64 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