diff --git a/composer.json b/composer.json
index 3cd2f7c03c622cbf10d68db98bc3113a034db688..9ac179e37dbd75f34c6b0904d33ad85ca39eeeb6 100644
--- a/composer.json
+++ b/composer.json
@@ -50,7 +50,7 @@
         "ext-mbstring": "*",
         "gettext/gettext": "^4.6",
         "phpmailer/phpmailer": "^6.1",
-        "simplesamlphp/assert": "^0.2.6",
+        "simplesamlphp/assert": "^0.2.7",
         "simplesamlphp/saml2": "^4.2.3",
         "simplesamlphp/twig-configurable-i18n": "^2.3",
         "symfony/cache": "^4.0 || ^5.0",
diff --git a/composer.lock b/composer.lock
index aeaa4a41df7f7ab558ea43cf522624660e497e28..da5665c1237914022f8aed2a55b939f93c92fd79 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "3f7b1c658a21ed99cf2359c5ac11761f",
+    "content-hash": "3454198cab8a477aa3697b77ec51f1a9",
     "packages": [
         {
             "name": "gettext/gettext",
@@ -469,22 +469,22 @@
         },
         {
             "name": "simplesamlphp/assert",
-            "version": "v0.2.6",
+            "version": "v0.2.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/simplesamlphp/assert.git",
-                "reference": "fbbf5df9f7d25c2049ad4681d64262abec095c40"
+                "reference": "d1d568ac85c133b7ec517cb00e7eda7f6a40e063"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/simplesamlphp/assert/zipball/fbbf5df9f7d25c2049ad4681d64262abec095c40",
-                "reference": "fbbf5df9f7d25c2049ad4681d64262abec095c40",
+                "url": "https://api.github.com/repos/simplesamlphp/assert/zipball/d1d568ac85c133b7ec517cb00e7eda7f6a40e063",
+                "reference": "d1d568ac85c133b7ec517cb00e7eda7f6a40e063",
                 "shasum": ""
             },
             "require": {
                 "ext-spl": "*",
                 "php": "^7.4 || ^8.0",
-                "webmozart/assert": "^1.9"
+                "webmozart/assert": "^1.10"
             },
             "require-dev": {
                 "simplesamlphp/simplesamlphp-test-framework": "^1.0.5"
@@ -517,9 +517,9 @@
             "description": "A wrapper around webmozart/assert to make it useful beyond checking method arguments",
             "support": {
                 "issues": "https://github.com/simplesamlphp/assert/issues",
-                "source": "https://github.com/simplesamlphp/assert/tree/v0.2.6"
+                "source": "https://github.com/simplesamlphp/assert/tree/v0.2.7"
             },
-            "time": "2021-04-07T17:19:30+00:00"
+            "time": "2021-07-24T19:23:31+00:00"
         },
         {
             "name": "simplesamlphp/saml2",
@@ -6482,5 +6482,5 @@
     "platform-dev": {
         "ext-curl": "*"
     },
-    "plugin-api-version": "2.0.0"
+    "plugin-api-version": "2.1.0"
 }
diff --git a/docs/simplesamlphp-theming.md b/docs/simplesamlphp-theming.md
index 7cc62a777b6aa7254fdd6a127a599a266dfd7162..e9b8c09eb2aca3f290fc045cf6121af3cb917592 100644
--- a/docs/simplesamlphp-theming.md
+++ b/docs/simplesamlphp-theming.md
@@ -22,19 +22,23 @@ If you want to customize the UI, the right way to do that is to create a new **t
 
 ### Configuring which theme to use
 
-In `config.php` there is a configuration option that controls theming. Here is an example:
+In `config.php` there is a configuration option that controls theming. You need to set that option and enable the module that contains the theme. Here is an example:
 
-	'theme.use' 		=> 'fancymodule:fancytheme',
+     'module.enable' => [
+         ...
+         'fancymodule' => true,
+     ],
 
-The `theme.use` parameter points to which theme that will be used. If some functionality in SimpleSAMLphp needs to present UI in example with the `logout.php` template, it will first look for `logout.php` in the `theme.use` theme, and if not found it will all fallback to look for the base templates.
+     'theme.use' => 'fancymodule:fancytheme',
 
-All required templates SHOULD be available as a base in the `templates` folder, and you SHOULD never change the base templates. To customize UI, add a new theme within a module that overrides the base templates, instead of modifying it.
+The `theme.use` parameter points to which theme that will be used. If some functionality in SimpleSAMLphp needs to present UI in example with the `logout.twig` template, it will first look for `logout.twig` in the `theme.use` theme, and if not found it will all fallback to look for the base templates.
 
-### Templates that include other files
+All required templates SHOULD be available as a base in the `templates` folder, and you SHOULD never change the base templates. To customize UI, add a new theme within a module that overrides the base templates, instead of modifying it.
 
-A template file may *include* other files. For example all the default templates will include a header and footer: the `login.php` template will first include `includes/header.php` then present the login page, and then include `includes/footer.php`.
+### Override only specific templates
 
-SimpleSAMLphp allows themes to override the included templates files only, if needed. That means you can create a new theme `fancytheme` that includes only a header and footer. The header file refers to the CSS files, which means that a simple way of making a new look on SimpleSAMLphp is to create a new theme, and copy the existing header, but point to your own CSS instead of the default CSS.
+The SimpleSAMLphp templates are derived from a base template and include other templates as building blocks. You only need to override the templates or building blocks needed for your change.
+SimpleSAMLphp allows themes to override the included templates files only, if needed. That means you can create a new theme `fancytheme` that includes only a header and footer template. These templates may refer to your own CSS files, which means that a simple way of making a new look on SimpleSAMLphp is to create a new theme, and copy the existing header, but point to your own CSS instead of the default CSS.
 
 
 Creating your first theme
@@ -50,51 +54,22 @@ The first thing you need to do is having a SimpleSAMLphp module to place your th
 Then within this module, you can create a new theme named `fancytheme`.
 
 	cd modules/mymodule
-	mkdir -p themes/fancytheme/default/includes
+	mkdir -p themes/fancytheme/default/
 
 Now, configure SimpleSAMLphp to use your new theme in `config.php`:
 
 	'theme.use' 		=> 'mymodule:fancytheme',
 
-Next, we create `themes/fancytheme/default/includes`, and copy the header file from the base theme:
-
-	cp templates/includes/header.php modules/mymodule/themes/fancytheme/default/includes/
-
-In the `modules/mymodule/themes/fancytheme/default/includes/header.php` type in something and go to the SimpleSAMLphp front page to see that your new theme is in use.
-
-A good start is to modify the reference to the default CSS:
-
-	<link rel="stylesheet" type="text/css" href="/<?php echo $this->data['baseurlpath']; ?>resources/default.css" />
-
-to in example:
-
-	<link rel="stylesheet" type="text/css" href="/<?php echo $this->data['baseurlpath']; ?>resources/fancytheme/default.css" />
-
-
-Examples
----------------------
-
-To override the frontpage body, add the file:
-
-	modules/mymodule/themes/fancytheme/default/frontpage.php
-
-In the path above `default` means that the frontpage template is not part of any modules. If you are replacing a template that is part of a module, then use the module name instead of `default`.
-
-For example, to override the `preprodwarning` template, (the file is located in `modules/preprodwarning/templates/warning.php`), you need to add a new file:
-
-	modules/mymodule/themes/fancytheme/preprodwarning/warning.php
-
+Next, we copy the header file from the base theme:
 
-Say in a module `foomodule`, some code requests to present the `bar.php` template, SimpleSAMLphp will:
-
- 1. first look in your theme for a replacement: `modules/mymodule/themes/fancytheme/foomodule/bar.php`.
- 2. If not found, it will use the base template of that module: `modules/foomodule/templates/bar.php`
+	cp templates/_header.twig modules/mymodule/themes/fancytheme/default/
 
+In the `modules/mymodule/themes/fancytheme/default/includes/_header.twig` file type in something and go to the SimpleSAMLphp front page to see that your new theme is in use.
 
 Adding resource files
 ---------------------
 
-You can put resource files within the www folder of your module, to make your module completely independent with included css, icons etc.
+You can put resource files within the `www/assets` folder of your module, to make your module completely independent with included css, icons etc.
 
 ```
 modules
@@ -102,18 +77,17 @@ modules
     └───lib
     └───themes
     └───www
-        └───logo.png
-        └───style.css
-```
-
-Reference these resources in your custom PHP templates under `themes/fancytheme` by using a generator for the URL:
-```
-<?php echo SimpleSAML\Module::getModuleURL('mymodule/logo.png'); ?>
+        └───assets
+            └───logo.png
+            └───style.css
 ```
 
+Reference these resources in your custom templates under `themes/fancytheme` by using a generator for the URL.
 Example for a custom CSS stylesheet file:
 ```
-<link rel="stylesheet" href="<?php echo SimpleSAML\Module::getModuleURL('mymodule/style.css'); ?>">
+{% block preload %}
+<link rel="stylesheet" href="{{ asset('style.css', 'mymodule') }}">
+{% endblock %}
 ```
 
 Migrating to Twig templates
@@ -129,7 +103,7 @@ If you need to make more extensive customizations to the base template, you shou
 
 	cp templates/base.twig modules/mymodule/themes/fancytheme/default/
 
-Any references to `$this->data['baseurlpath']` in old-style templates can be replaced with `{{baseurlpath}}` in Twig templates. Likewise, references to `\SimpleSAML\Module::getModuleURL()` can be replaced with `{{baseurlpath}}module.php/mymodule/...`
+Any references to `$this->data['baseurlpath']` in old-style templates can be replaced with `{{baseurlpath}}` in Twig templates. Likewise, references to `\SimpleSAML\Module::getModuleURL()` can be replaced with `{{baseurlpath}}module.php/mymodule/...` or the `asset()` function like shown above.
 
 Within templates each module is defined as a separate namespace matching the module name. This allows one template to reference templates from other modules using Twig's `@namespace_name/template_path` notation. For instance, a template in `mymodule` can include the widget template from the `yourmodule` module using the notation `@yourmodule/widget.twig`. A special namespace, `__parent__`, exists to allow theme developers to more easily extend a module's stock template.
 
diff --git a/lib/SimpleSAML/Auth/ProcessingFilter.php b/lib/SimpleSAML/Auth/ProcessingFilter.php
index a83c348b158ac04d9ac35252bcc0ce0f6f30bf2d..65827c6dfd5491c9749534eba5a81871fe17e667 100644
--- a/lib/SimpleSAML/Auth/ProcessingFilter.php
+++ b/lib/SimpleSAML/Auth/ProcessingFilter.php
@@ -60,7 +60,7 @@ abstract class ProcessingFilter
      *
      * When a filter returns from this function, it is assumed to have completed its task.
      *
-     * @param array &$request  The request we are currently processing.
+     * @param array &$state  The request we are currently processing.
      */
-    abstract public function process(array &$request): void;
+    abstract public function process(array &$state): void;
 }
diff --git a/lib/SimpleSAML/Error/CriticalConfigurationError.php b/lib/SimpleSAML/Error/CriticalConfigurationError.php
index 9add0d009dc3007efb1dcdfb20243b18e478cd8e..04b2a044862f45afaaa20efbcbc6135ac3995b2a 100644
--- a/lib/SimpleSAML/Error/CriticalConfigurationError.php
+++ b/lib/SimpleSAML/Error/CriticalConfigurationError.php
@@ -67,19 +67,19 @@ class CriticalConfigurationError extends ConfigurationError
 
 
     /**
-     * @param \Throwable $exception
+     * @param \Throwable $e
      *
      * @return \SimpleSAML\Error\CriticalConfigurationError
      */
-    public static function fromException(Throwable $exception): CriticalConfigurationError
+    public static function fromException(Throwable $e): CriticalConfigurationError
     {
         $reason = null;
         $file = null;
-        if ($exception instanceof ConfigurationError) {
-            $reason = $exception->getReason();
-            $file = $exception->getConfFile();
+        if ($e instanceof ConfigurationError) {
+            $reason = $e->getReason();
+            $file = $e->getConfFile();
         } else {
-            $reason = $exception->getMessage();
+            $reason = $e->getMessage();
         }
         return new CriticalConfigurationError($reason, $file);
     }
diff --git a/lib/SimpleSAML/Locale/Localization.php b/lib/SimpleSAML/Locale/Localization.php
index 21c606062789dee2388e4e08e4d86b1196d2eb53..ae621c2cfb64ac1d8afaa11068da96bba292d271 100644
--- a/lib/SimpleSAML/Locale/Localization.php
+++ b/lib/SimpleSAML/Locale/Localization.php
@@ -206,7 +206,7 @@ class Localization
         }
 
         // Locale for default language missing even, error out
-        $error = "Localization directory missing/broken for langcode '$langcode' and domain '$domain'";
+        $error = "Localization directory '$langPath' missing/broken for langcode '$langcode' and domain '$domain'";
         Logger::critical($_SERVER['PHP_SELF'] . ' - ' . $error);
         throw new \Exception($error);
     }
diff --git a/lib/SimpleSAML/Locale/Translate.php b/lib/SimpleSAML/Locale/Translate.php
index 9285aaf90eb81dbf7efc6dde406d9c75a9a1d436..4c26b670da39dd9cf51e955c089fb02765b85c56 100644
--- a/lib/SimpleSAML/Locale/Translate.php
+++ b/lib/SimpleSAML/Locale/Translate.php
@@ -143,45 +143,6 @@ class Translate
     }
 
 
-    /**
-     * Retrieve the preferred translation of a given text.
-     *
-     * @param array $translations The translations, as an associative array with language => text mappings.
-     *
-     * @return string The preferred translation.
-     *
-     * @throws \Exception If there's no suitable translation.
-     */
-    public function getPreferredTranslation(array $translations): string
-    {
-        // look up translation of tag in the selected language
-        $selected_language = $this->language->getLanguage();
-        if (array_key_exists($selected_language, $translations)) {
-            return $translations[$selected_language];
-        }
-
-        // look up translation of tag in the default language
-        $default_language = $this->language->getDefaultLanguage();
-        if (array_key_exists($default_language, $translations)) {
-            return $translations[$default_language];
-        }
-
-        // check for english translation
-        if (array_key_exists('en', $translations)) {
-            return $translations['en'];
-        }
-
-        // pick the first translation available
-        if (count($translations) > 0) {
-            $languages = array_keys($translations);
-            return $translations[$languages[0]];
-        }
-
-        // we don't have anything to return
-        throw new \Exception('Nothing to return from translation.');
-    }
-
-
     /**
      * Translate the name of an attribute.
      *
diff --git a/lib/SimpleSAML/XHTML/Template.php b/lib/SimpleSAML/XHTML/Template.php
index e75907e636441bab1a95e2a77ea1b2c682877350..f9df3c72cda1e154ec698ec6afb8b96507b8fbcf 100644
--- a/lib/SimpleSAML/XHTML/Template.php
+++ b/lib/SimpleSAML/XHTML/Template.php
@@ -351,11 +351,9 @@ class Template extends Response
 
         // setup directories & namespaces
         $themeDir = Module::getModuleDir($this->theme['module']) . '/themes/' . $this->theme['name'];
-        $subdirs = scandir($themeDir);
+        $subdirs = @scandir($themeDir);
         if (empty($subdirs)) {
-            // no subdirectories in the theme directory, nothing to do here
-            // this is probably wrong, log a message
-            Logger::warning('Empty theme directory for theme "' . $this->theme['name'] . '".');
+            Logger::warning('Theme directory for theme "' . $this->theme['name'] . '" (' . $themeDir . ') is not readable or is empty.');
             return [];
         }
 
diff --git a/modules/admin/lib/Controller/Test.php b/modules/admin/lib/Controller/Test.php
index b93e80e6b50a7f703af020a977db63bd3fa90c90..383debf0a8a696d65b274f7c5bddb5eabbfeedcc 100644
--- a/modules/admin/lib/Controller/Test.php
+++ b/modules/admin/lib/Controller/Test.php
@@ -180,10 +180,7 @@ class Test
             "NameId" => [$nameId->getValue()],
         ];
         if ($nameId->getFormat() !== null) {
-            $format = $translator->getPreferredTranslation(
-                $translator->getTag('{status:subject_format}') ?? ['en' => 'Format']
-            );
-            $list[$format] = [$nameId->getFormat()];
+            $list['Format'] = [$nameId->getFormat()];
         }
         if ($nameId->getNameQualifier() !== null) {
             $list['NameQualifier'] = [$nameId->getNameQualifier()];
@@ -327,10 +324,7 @@ class Test
             'NameID' => [$nameID->getValue()],
         ];
         if ($nameID->getFormat() !== null) {
-            $format = $t->getPreferredTranslation(
-                $t->getTag('{status:subject_format}') ?? ['en' => 'Format']
-            );
-            $eptid[$format] = [$nameID->getFormat()];
+            $eptid['Format'] = [$nameID->getFormat()];
         }
         if ($nameID->getNameQualifier() !== null) {
             $eptid['NameQualifier'] = [$nameID->getNameQualifier()];
diff --git a/modules/core/lib/Auth/Process/AttributeAdd.php b/modules/core/lib/Auth/Process/AttributeAdd.php
index d30084a8738a59201f39f8c7e14d9d92b0ccab9a..10c571fb5b761c0771372454a9daa5167e8e575b 100644
--- a/modules/core/lib/Auth/Process/AttributeAdd.php
+++ b/modules/core/lib/Auth/Process/AttributeAdd.php
@@ -73,13 +73,13 @@ class AttributeAdd extends Auth\ProcessingFilter
      *
      * Add or replace existing attributes with the configured values.
      *
-     * @param array &$request  The current request
+     * @param array &$state  The current request
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        Assert::keyExists($request, 'Attributes');
+        Assert::keyExists($state, 'Attributes');
 
-        $attributes = &$request['Attributes'];
+        $attributes = &$state['Attributes'];
 
         foreach ($this->attributes as $name => $values) {
             if ($this->replace === true || !array_key_exists($name, $attributes)) {
diff --git a/modules/core/lib/Auth/Process/AttributeAlter.php b/modules/core/lib/Auth/Process/AttributeAlter.php
index 29a2c0823aa7aae0041d2c46217cf20168875147..e6db082d689f69639292666a4b0e367c33b7fc6e 100644
--- a/modules/core/lib/Auth/Process/AttributeAlter.php
+++ b/modules/core/lib/Auth/Process/AttributeAlter.php
@@ -99,15 +99,15 @@ class AttributeAlter extends Auth\ProcessingFilter
      *
      * Modify existing attributes with the configured values.
      *
-     * @param array &$request The current request.
+     * @param array &$state The current request.
      * @throws \SimpleSAML\Error\Exception In case of invalid configuration.
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        Assert::keyExists($request, 'Attributes');
+        Assert::keyExists($state, 'Attributes');
 
         // get attributes from request
-        $attributes = &$request['Attributes'];
+        $attributes = &$state['Attributes'];
 
         // check that all required params are set in config
         if (empty($this->pattern) || empty($this->subject)) {
diff --git a/modules/core/lib/Auth/Process/AttributeCopy.php b/modules/core/lib/Auth/Process/AttributeCopy.php
index 199e4a8a24464f419878d27891bd638f4544a0eb..31c9297675b925ccb884f6d85c17d4a238320cd1 100644
--- a/modules/core/lib/Auth/Process/AttributeCopy.php
+++ b/modules/core/lib/Auth/Process/AttributeCopy.php
@@ -58,13 +58,13 @@ class AttributeCopy extends Auth\ProcessingFilter
     /**
      * Apply filter to rename attributes.
      *
-     * @param array &$request  The current request
+     * @param array &$state  The current request
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        Assert::keyExists($request, 'Attributes');
+        Assert::keyExists($state, 'Attributes');
 
-        $attributes = &$request['Attributes'];
+        $attributes = &$state['Attributes'];
 
         foreach ($attributes as $name => $values) {
             if (array_key_exists($name, $this->map)) {
diff --git a/modules/core/lib/Auth/Process/AttributeLimit.php b/modules/core/lib/Auth/Process/AttributeLimit.php
index 490940f3084293df10e530658373634bac353679..e2f3c5041ae4c1b6f1c9248d19e1963d6af75db9 100644
--- a/modules/core/lib/Auth/Process/AttributeLimit.php
+++ b/modules/core/lib/Auth/Process/AttributeLimit.php
@@ -64,18 +64,18 @@ class AttributeLimit extends Auth\ProcessingFilter
     /**
      * Get list of allowed from the SP/IdP config.
      *
-     * @param array &$request  The current request.
+     * @param array &$state  The current request.
      * @return array|null  Array with attribute names, or NULL if no limit is placed.
      */
-    private static function getSPIdPAllowed(array &$request): ?array
+    private static function getSPIdPAllowed(array &$state): ?array
     {
-        if (array_key_exists('attributes', $request['Destination'])) {
+        if (array_key_exists('attributes', $state['Destination'])) {
             // SP Config
-            return $request['Destination']['attributes'];
+            return $state['Destination']['attributes'];
         }
-        if (array_key_exists('attributes', $request['Source'])) {
+        if (array_key_exists('attributes', $state['Source'])) {
             // IdP Config
-            return $request['Source']['attributes'];
+            return $state['Source']['attributes'];
         }
         return null;
     }
@@ -86,29 +86,29 @@ class AttributeLimit extends Auth\ProcessingFilter
      *
      * Removes all attributes which aren't one of the allowed attributes.
      *
-     * @param array &$request  The current request
+     * @param array &$state  The current request
      * @throws \SimpleSAML\Error\Exception If invalid configuration is found.
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        assert::keyExists($request, 'Attributes');
+        assert::keyExists($state, 'Attributes');
 
         if ($this->isDefault) {
-            $allowedAttributes = self::getSPIdPAllowed($request);
+            $allowedAttributes = self::getSPIdPAllowed($state);
             if ($allowedAttributes === null) {
                 $allowedAttributes = $this->allowedAttributes;
             }
         } elseif (!empty($this->allowedAttributes)) {
             $allowedAttributes = $this->allowedAttributes;
         } else {
-            $allowedAttributes = self::getSPIdPAllowed($request);
+            $allowedAttributes = self::getSPIdPAllowed($state);
             if ($allowedAttributes === null) {
                 // No limit on attributes
                 return;
             }
         }
 
-        $attributes = &$request['Attributes'];
+        $attributes = &$state['Attributes'];
 
         foreach ($attributes as $name => $values) {
             if (!in_array($name, $allowedAttributes, true)) {
diff --git a/modules/core/lib/Auth/Process/AttributeMap.php b/modules/core/lib/Auth/Process/AttributeMap.php
index 3fd36bdae63c4c997d8df4d17133569052e6bccc..16cf735ddbc1285e98f175df17d298ea75079ba1 100644
--- a/modules/core/lib/Auth/Process/AttributeMap.php
+++ b/modules/core/lib/Auth/Process/AttributeMap.php
@@ -115,15 +115,15 @@ class AttributeMap extends Auth\ProcessingFilter
     /**
      * Apply filter to rename attributes.
      *
-     * @param array &$request The current request.
+     * @param array &$state The current request.
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        Assert::keyExists($request, 'Attributes');
+        Assert::keyExists($state, 'Attributes');
 
         $mapped_attributes = [];
 
-        foreach ($request['Attributes'] as $name => $values) {
+        foreach ($state['Attributes'] as $name => $values) {
             if (array_key_exists($name, $this->map)) {
                 if (!is_array($this->map[$name])) {
                     if ($this->duplicate) {
@@ -146,6 +146,6 @@ class AttributeMap extends Auth\ProcessingFilter
             }
         }
 
-        $request['Attributes'] = $mapped_attributes;
+        $state['Attributes'] = $mapped_attributes;
     }
 }
diff --git a/modules/core/lib/Auth/Process/AttributeValueMap.php b/modules/core/lib/Auth/Process/AttributeValueMap.php
index 26636d35bb4d020a40d1c76d9bc0a77e912370dd..e4457633b0fe16ece896f4762069c42add634cc5 100644
--- a/modules/core/lib/Auth/Process/AttributeValueMap.php
+++ b/modules/core/lib/Auth/Process/AttributeValueMap.php
@@ -107,14 +107,14 @@ class AttributeValueMap extends Auth\ProcessingFilter
     /**
      * Apply filter.
      *
-     * @param array &$request The current request
+     * @param array &$state The current request
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
         Logger::debug('Processing the AttributeValueMap filter.');
 
-        Assert::keyExists($request, 'Attributes');
-        $attributes = &$request['Attributes'];
+        Assert::keyExists($state, 'Attributes');
+        $attributes = &$state['Attributes'];
 
         if (!array_key_exists($this->sourceattribute, $attributes)) {
             // the source attribute does not exist, nothing to do here
diff --git a/modules/core/lib/Auth/Process/Cardinality.php b/modules/core/lib/Auth/Process/Cardinality.php
index 0a15500e0084f7419f8dbfa3d6c48b3ad13865f7..bbc3e4603564dc3a872dccf5fa410270c986d6b4 100644
--- a/modules/core/lib/Auth/Process/Cardinality.php
+++ b/modules/core/lib/Auth/Process/Cardinality.php
@@ -106,22 +106,22 @@ class Cardinality extends Auth\ProcessingFilter
     /**
      * Process this filter
      *
-     * @param array &$request  The current request
+     * @param array &$state  The current request
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        Assert::keyExists($request, 'Attributes');
+        Assert::keyExists($state, 'Attributes');
 
         $entityid = false;
-        if (array_key_exists('Source', $request) && array_key_exists('entityid', $request['Source'])) {
-            $entityid = $request['Source']['entityid'];
+        if (array_key_exists('Source', $state) && array_key_exists('entityid', $state['Source'])) {
+            $entityid = $state['Source']['entityid'];
         }
         if (in_array($entityid, $this->ignoreEntities, true)) {
             Logger::debug('Cardinality: Ignoring assertions from ' . $entityid);
             return;
         }
 
-        foreach ($request['Attributes'] as $k => $v) {
+        foreach ($state['Attributes'] as $k => $v) {
             if (!array_key_exists($k, $this->cardinality)) {
                 continue;
             }
@@ -142,7 +142,7 @@ class Cardinality extends Auth\ProcessingFilter
                         )
                     );
                 } else {
-                    $request['core:cardinality:errorAttributes'][$k] = [
+                    $state['core:cardinality:errorAttributes'][$k] = [
                         count($v),
                         $this->cardinality[$k]['_expr']
                     ];
@@ -163,7 +163,7 @@ class Cardinality extends Auth\ProcessingFilter
                         )
                     );
                 } else {
-                    $request['core:cardinality:errorAttributes'][$k] = [
+                    $state['core:cardinality:errorAttributes'][$k] = [
                         count($v),
                         $this->cardinality[$k]['_expr']
                     ];
@@ -174,7 +174,7 @@ class Cardinality extends Auth\ProcessingFilter
 
         /* check for missing attributes with a minimum cardinality */
         foreach ($this->cardinality as $k => $v) {
-            if (!$this->cardinality[$k]['min'] || array_key_exists($k, $request['Attributes'])) {
+            if (!$this->cardinality[$k]['min'] || array_key_exists($k, $state['Attributes'])) {
                 continue;
             }
             if ($this->cardinality[$k]['warn']) {
@@ -184,7 +184,7 @@ class Cardinality extends Auth\ProcessingFilter
                     $entityid
                 ));
             } else {
-                $request['core:cardinality:errorAttributes'][$k] = [
+                $state['core:cardinality:errorAttributes'][$k] = [
                     0,
                     $this->cardinality[$k]['_expr']
                 ];
@@ -192,8 +192,8 @@ class Cardinality extends Auth\ProcessingFilter
         }
 
         /* abort if we found a problematic attribute */
-        if (array_key_exists('core:cardinality:errorAttributes', $request)) {
-            $id = Auth\State::saveState($request, 'core:cardinality');
+        if (array_key_exists('core:cardinality:errorAttributes', $state)) {
+            $id = Auth\State::saveState($state, 'core:cardinality');
             $url = Module::getModuleURL('core/cardinality_error.php');
             $this->httpUtils->redirectTrustedURL($url, ['StateId' => $id]);
             return;
diff --git a/modules/core/lib/Auth/Process/CardinalitySingle.php b/modules/core/lib/Auth/Process/CardinalitySingle.php
index 64c5e059583bd511c62c4a292e66c1da79db3ebf..4b31a2b456c8663be5ca566011a86b6aad92100a 100644
--- a/modules/core/lib/Auth/Process/CardinalitySingle.php
+++ b/modules/core/lib/Auth/Process/CardinalitySingle.php
@@ -81,22 +81,22 @@ class CardinalitySingle extends Auth\ProcessingFilter
     /**
      * Process this filter
      *
-     * @param array &$request  The current request
+     * @param array &$state  The current request
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        Assert::keyExists($request, 'Attributes');
+        Assert::keyExists($state, 'Attributes');
 
         if (
-            array_key_exists('Source', $request)
-            && array_key_exists('entityid', $request['Source'])
-            && in_array($request['Source']['entityid'], $this->ignoreEntities, true)
+            array_key_exists('Source', $state)
+            && array_key_exists('entityid', $state['Source'])
+            && in_array($state['Source']['entityid'], $this->ignoreEntities, true)
         ) {
-            Logger::debug('CardinalitySingle: Ignoring assertions from ' . $request['Source']['entityid']);
+            Logger::debug('CardinalitySingle: Ignoring assertions from ' . $state['Source']['entityid']);
             return;
         }
 
-        foreach ($request['Attributes'] as $k => $v) {
+        foreach ($state['Attributes'] as $k => $v) {
             if (!is_array($v)) {
                 continue;
             }
@@ -105,22 +105,22 @@ class CardinalitySingle extends Auth\ProcessingFilter
             }
 
             if (in_array($k, $this->singleValued)) {
-                $request['core:cardinality:errorAttributes'][$k] = [count($v), '0 ≤ n ≤ 1'];
+                $state['core:cardinality:errorAttributes'][$k] = [count($v), '0 ≤ n ≤ 1'];
                 continue;
             }
             if (in_array($k, $this->firstValue)) {
-                $request['Attributes'][$k] = [array_shift($v)];
+                $state['Attributes'][$k] = [array_shift($v)];
                 continue;
             }
             if (in_array($k, $this->flatten)) {
-                $request['Attributes'][$k] = [implode($this->flattenWith, $v)];
+                $state['Attributes'][$k] = [implode($this->flattenWith, $v)];
                 continue;
             }
         }
 
         /* abort if we found a problematic attribute */
-        if (array_key_exists('core:cardinality:errorAttributes', $request)) {
-            $id = Auth\State::saveState($request, 'core:cardinality');
+        if (array_key_exists('core:cardinality:errorAttributes', $state)) {
+            $id = Auth\State::saveState($state, 'core:cardinality');
             $url = Module::getModuleURL('core/cardinality_error.php');
             $this->httpUtils->redirectTrustedURL($url, ['StateId' => $id]);
             return;
diff --git a/modules/core/lib/Auth/Process/GenerateGroups.php b/modules/core/lib/Auth/Process/GenerateGroups.php
index 2a8eadef7a1930a5e7e180ddfbd60e7954983aea..89e8b4c21cb25fde994d6c513730bb5fb6ff6956 100644
--- a/modules/core/lib/Auth/Process/GenerateGroups.php
+++ b/modules/core/lib/Auth/Process/GenerateGroups.php
@@ -56,14 +56,14 @@ class GenerateGroups extends Auth\ProcessingFilter
     /**
      * Apply filter to add groups attribute.
      *
-     * @param array &$request  The current request
+     * @param array &$state  The current request
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        Assert::keyExists($request, 'Attributes');
+        Assert::keyExists($state, 'Attributes');
 
         $groups = [];
-        $attributes = &$request['Attributes'];
+        $attributes = &$state['Attributes'];
 
         $realm = self::getRealm($attributes);
         if ($realm !== null) {
diff --git a/modules/core/lib/Auth/Process/LanguageAdaptor.php b/modules/core/lib/Auth/Process/LanguageAdaptor.php
index c621172e6f61129548a03c457c34b3618b2c236d..d38588e2eaadce68202b9b4556128bb70eabb473 100644
--- a/modules/core/lib/Auth/Process/LanguageAdaptor.php
+++ b/modules/core/lib/Auth/Process/LanguageAdaptor.php
@@ -41,13 +41,13 @@ class LanguageAdaptor extends Auth\ProcessingFilter
      *
      * Add or replace existing attributes with the configured values.
      *
-     * @param array &$request  The current request
+     * @param array &$state  The current request
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        Assert::keyExists($request, 'Attributes');
+        Assert::keyExists($state, 'Attributes');
 
-        $attributes = &$request['Attributes'];
+        $attributes = &$state['Attributes'];
 
         $attrlang = null;
         if (array_key_exists($this->langattr, $attributes)) {
@@ -68,7 +68,7 @@ class LanguageAdaptor extends Auth\ProcessingFilter
             Language::setLanguageCookie($attrlang);
         } elseif (!isset($attrlang) && isset($lang)) {
             // Language set in cookie, but not in attribute. Update attribute
-            $request['Attributes'][$this->langattr] = [$lang];
+            $state['Attributes'][$this->langattr] = [$lang];
         }
     }
 }
diff --git a/modules/core/lib/Auth/Process/PHP.php b/modules/core/lib/Auth/Process/PHP.php
index 15428a461a4cce69e45f34cc1704fe8c58d1df7c..d2005eec3667ba21cd423fa28969e4c7146880a1 100644
--- a/modules/core/lib/Auth/Process/PHP.php
+++ b/modules/core/lib/Auth/Process/PHP.php
@@ -46,13 +46,11 @@ class PHP extends Auth\ProcessingFilter
     /**
      * Apply the PHP code to the attributes.
      *
-     * @param array &$request The current request
-     *
-     * @scrutinizer ignore-unused
+     * @param array &$state The current request
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        Assert::keyExists($request, 'Attributes');
+        Assert::keyExists($state, 'Attributes');
 
         /**
          * @param array &$attributes
@@ -64,6 +62,6 @@ class PHP extends Auth\ProcessingFilter
         ) {
             eval($this->code);
         };
-        $function($request['Attributes'], $request);
+        $function($state['Attributes'], $state);
     }
 }
diff --git a/modules/core/lib/Auth/Process/ScopeAttribute.php b/modules/core/lib/Auth/Process/ScopeAttribute.php
index 18955f9f64fbc171c3857504f4f3cf49dca18025..aec51485d558bab25749c58de9c564abf12e0ac4 100644
--- a/modules/core/lib/Auth/Process/ScopeAttribute.php
+++ b/modules/core/lib/Auth/Process/ScopeAttribute.php
@@ -67,13 +67,13 @@ class ScopeAttribute extends Auth\ProcessingFilter
     /**
      * Apply this filter to the request.
      *
-     * @param array &$request  The current request
+     * @param array &$state  The current request
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        Assert::keyExists($request, 'Attributes');
+        Assert::keyExists($state, 'Attributes');
 
-        $attributes = &$request['Attributes'];
+        $attributes = &$state['Attributes'];
 
         if (!isset($attributes[$this->scopeAttribute])) {
             return;
diff --git a/modules/core/lib/Auth/Process/ScopeFromAttribute.php b/modules/core/lib/Auth/Process/ScopeFromAttribute.php
index 7734cacb42b2ba35107cb8aff953e2dbe86ec710..be54edaea628360f2633f3bdeea64bd1e0a3896f 100644
--- a/modules/core/lib/Auth/Process/ScopeFromAttribute.php
+++ b/modules/core/lib/Auth/Process/ScopeFromAttribute.php
@@ -61,13 +61,13 @@ class ScopeFromAttribute extends Auth\ProcessingFilter
     /**
      * Apply this filter.
      *
-     * @param array &$request  The current request
+     * @param array &$state  The current request
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        Assert::keyExists($request, 'Attributes');
+        Assert::keyExists($state, 'Attributes');
 
-        $attributes = &$request['Attributes'];
+        $attributes = &$state['Attributes'];
 
         if (!isset($attributes[$this->sourceAttribute])) {
             return;
diff --git a/modules/exampleauth/lib/Auth/Source/External.php b/modules/exampleauth/lib/Auth/Source/External.php
index 2e8830fdf688ab2860df457e08a72c0d2723fb34..d369fc71348def32ee15b3d788ca20115add94a0 100644
--- a/modules/exampleauth/lib/Auth/Source/External.php
+++ b/modules/exampleauth/lib/Auth/Source/External.php
@@ -195,7 +195,7 @@ class External extends Auth\Source
          * First we need to restore the $state-array. We should have the identifier for
          * it in the 'State' request parameter.
          */
-        if (!$request->has('State')) {
+        if (!$request->query->has('State')) {
             throw new Error\BadRequest('Missing "State" parameter.');
         }
 
diff --git a/modules/saml/lib/Auth/Process/ExpectedAuthnContextClassRef.php b/modules/saml/lib/Auth/Process/ExpectedAuthnContextClassRef.php
index 2ffd641a6695540280fa9b5c842a93311d743f61..103d618e7a76acd7d9376e5845680f0e7879241c 100644
--- a/modules/saml/lib/Auth/Process/ExpectedAuthnContextClassRef.php
+++ b/modules/saml/lib/Auth/Process/ExpectedAuthnContextClassRef.php
@@ -69,17 +69,16 @@ class ExpectedAuthnContextClassRef extends ProcessingFilter
 
 
     /**
-     *
-     * @param array &$request The current request
+     * @param array &$state The current request
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        Assert::keyExists($request, 'Attributes');
+        Assert::keyExists($state, 'Attributes');
 
-        $this->AuthnContextClassRef = $request['saml:sp:State']['saml:sp:AuthnContext'];
+        $this->AuthnContextClassRef = $state['saml:sp:State']['saml:sp:AuthnContext'];
 
         if (!in_array($this->AuthnContextClassRef, $this->accepted, true)) {
-            $this->unauthorized($request);
+            $this->unauthorized($state);
         }
     }
 
@@ -94,16 +93,16 @@ class ExpectedAuthnContextClassRef extends ProcessingFilter
      * thinking in case a "chained" ACL is needed, more complex
      * permission logic.
      *
-     * @param array $request
+     * @param array $state
      */
-    protected function unauthorized(array &$request): void
+    protected function unauthorized(array &$state): void
     {
         Logger::error(
             'ExpectedAuthnContextClassRef: Invalid authentication context: ' . strval($this->AuthnContextClassRef) .
             '. Accepted values are: ' . var_export($this->accepted, true)
         );
 
-        $id = Auth\State::saveState($request, 'saml:ExpectedAuthnContextClassRef:unauthorized');
+        $id = Auth\State::saveState($state, 'saml:ExpectedAuthnContextClassRef:unauthorized');
         $url = Module::getModuleURL(
             'saml/sp/wrong_authncontextclassref.php'
         );
diff --git a/modules/saml/lib/Auth/Process/FilterScopes.php b/modules/saml/lib/Auth/Process/FilterScopes.php
index b0fb75237d7fa7e4384af1bdc89fb6ad9436b655..13b9753702c5d33f4597507b3e447e5ccb3c83cc 100644
--- a/modules/saml/lib/Auth/Process/FilterScopes.php
+++ b/modules/saml/lib/Auth/Process/FilterScopes.php
@@ -43,27 +43,27 @@ class FilterScopes extends ProcessingFilter
     /**
      * This method applies the filter, removing any values
      *
-     * @param array &$request the current request
+     * @param array &$state the current request
      */
-    public function process(array &$request): void
+    public function process(array &$state): void
     {
-        $src = $request['Source'];
+        $src = $state['Source'];
 
         $validScopes = [];
         $host = '';
         if (array_key_exists('scope', $src) && is_array($src['scope']) && !empty($src['scope'])) {
             $validScopes = $src['scope'];
         } else {
-            $ep = Utils\Config\Metadata::getDefaultEndpoint($request['Source']['SingleSignOnService']);
+            $ep = Utils\Config\Metadata::getDefaultEndpoint($state['Source']['SingleSignOnService']);
             $host = parse_url($ep['Location'], PHP_URL_HOST) ?? '';
         }
 
         foreach ($this->scopedAttributes as $attribute) {
-            if (!isset($request['Attributes'][$attribute])) {
+            if (!isset($state['Attributes'][$attribute])) {
                 continue;
             }
 
-            $values = $request['Attributes'][$attribute];
+            $values = $state['Attributes'][$attribute];
             $newValues = [];
             foreach ($values as $value) {
                 @list(, $scope) = explode('@', $value, 2);
@@ -83,9 +83,9 @@ class FilterScopes extends ProcessingFilter
 
             if (empty($newValues)) {
                 Logger::warning("No suitable values for attribute '$attribute', removing it.");
-                unset($request['Attributes'][$attribute]); // remove empty attributes
+                unset($state['Attributes'][$attribute]); // remove empty attributes
             } else {
-                $request['Attributes'][$attribute] = $newValues;
+                $state['Attributes'][$attribute] = $newValues;
             }
         }
     }
diff --git a/modules/saml/lib/Error.php b/modules/saml/lib/Error.php
index 7a4655470615f4d24b028e88cce6310136377baa..d54d40dfe3bb65315210ca1d8710d71905fff2d3 100644
--- a/modules/saml/lib/Error.php
+++ b/modules/saml/lib/Error.php
@@ -108,20 +108,20 @@ class Error extends \SimpleSAML\Error\Exception
      * This function attempts to create a SAML2 error with the appropriate
      * status codes from an arbitrary exception.
      *
-     * @param \Throwable $exception  The original exception.
+     * @param \Throwable $e  The original exception.
      * @return \SimpleSAML\Error\Exception  The new exception.
      */
-    public static function fromException(Throwable $exception): \SimpleSAML\Error\Exception
+    public static function fromException(Throwable $e): \SimpleSAML\Error\Exception
     {
-        if ($exception instanceof \SimpleSAML\Module\saml\Error) {
+        if ($e instanceof \SimpleSAML\Module\saml\Error) {
             // Return the original exception unchanged
-            return $exception;
+            return $e;
         } else {
             $e = new self(
                 \SAML2\Constants::STATUS_RESPONDER,
                 null,
-                get_class($exception) . ': ' . $exception->getMessage(),
-                $exception
+                get_class($e) . ': ' . $e->getMessage(),
+                $e
             );
         }
 
diff --git a/modules/saml/templates/proxy/invalid_session.twig b/modules/saml/templates/proxy/invalid_session.twig
index f9acc7a9881e7b12c7b5ff432b80b091ee6dc6c7..593adca27ace9b143b6209ab459279102453ac0d 100644
--- a/modules/saml/templates/proxy/invalid_session.twig
+++ b/modules/saml/templates/proxy/invalid_session.twig
@@ -3,7 +3,7 @@
 
 {% block content %}
     <h2>{{ 'Invalid Identity Provider'|trans }}</h2>
-    <p>{{ 'You already have a valid session with an identity provider (<em>%IDP%</em>) that is not accepted by <em>%SP%</em>. Would you like to log out from your existing session and log in again with another identity provider?'|trans({"%IDP%": idp_name, "%SP%": sp_name}, "app")|raw</p>
+    <p>{{ 'You already have a valid session with an identity provider (<em>%IDP%</em>) that is not accepted by <em>%SP%</em>. Would you like to log out from your existing session and log in again with another identity provider?'|trans({"%IDP%": idp_name|translateFromArray|default(idp_entityid), "%SP%": sp_name|translateFromArray|default(sp_entityid)}, "app")|raw</p>
     <form method="post" action="?">
         <input type="hidden" name="AuthState" value="{{ AuthState|escape('html') }}" />
         <input type="submit" name="continue" value="{{ 'Yes, continue'|trans }}" />
diff --git a/modules/saml/www/proxy/invalid_session.php b/modules/saml/www/proxy/invalid_session.php
index 30c18095d309b9ece4d2a5ba71ad6a2207e440d7..9dd31b5250cd9e1dd9846df3dfa86c57a2d8f2c9 100644
--- a/modules/saml/www/proxy/invalid_session.php
+++ b/modules/saml/www/proxy/invalid_session.php
@@ -56,21 +56,19 @@ $idpmdcfg = $state['saml:sp:IdPMetadata'];
 /** @var \SimpleSAML\Configuration $idpmdcfg */
 $idpmd = $idpmdcfg->toArray();
 if (array_key_exists('name', $idpmd)) {
-    $template->data['idp_name'] = $translator->getPreferredTranslation($idpmd['name']);
+    $template->data['idp_name'] = $idpmd['name'];
 } elseif (array_key_exists('OrganizationDisplayName', $idpmd)) {
-    $template->data['idp_name'] = $translator->getPreferredTranslation($idpmd['OrganizationDisplayName']);
-} else {
-    $template->data['idp_name'] = $idpmd['entityid'];
+    $template->data['idp_name'] = $idpmd['OrganizationDisplayName'];
 }
+$template->data['idp_entityid'] = $idpmd['entityid'];
 
 // get the name of the SP
 $spmd = $state['SPMetadata'];
 if (array_key_exists('name', $spmd)) {
-    $template->data['sp_name'] = $translator->getPreferredTranslation($spmd['name']);
+    $template->data['sp_name'] = $spmd['name'];
 } elseif (array_key_exists('OrganizationDisplayName', $spmd)) {
-    $template->data['sp_name'] = $translator->getPreferredTranslation($spmd['OrganizationDisplayName']);
-} else {
-    $template->data['sp_name'] = $spmd['entityid'];
+    $template->data['sp_name'] = $spmd['OrganizationDisplayName'];
 }
+$template->data['sp_entityid'] = $spmd['entityid'];
 
 $template->send();