Browse Source

Merge pull request #6483 from PseudoResonance/oauth2-redirect-extra-domain

Allow additional domains in OAuth2 redirect URLs
FreddleSpl0it 3 months ago
parent
commit
e7a1f24c78

+ 6 - 0
data/web/api/openapi.yaml

@@ -5847,6 +5847,7 @@ paths:
                           client_id: "mailcow_client"
                           client_secret: "*"
                           redirect_url: "https://mail.mailcow.tld"
+                          redirect_url_extra: ["https://extramail.mailcow.tld"]
                           version: "26.1.3"
                           default_template: "Default"
                           mappers:
@@ -5900,6 +5901,9 @@ paths:
                     redirect_url:
                       description: The redirect URL that OIDC Provider will use after authentication. Required if `authsource` is keycloak or generic-oidc.
                       type: string
+                    redirect_url_extra:
+                      description: Additional redirect URLs that OIDC Provider can use after authentication if valid.
+                      type: array
                     version:
                       description: Specifies the Keycloak version. Required if `authsource` is keycloak.
                       type: string
@@ -5990,6 +5994,7 @@ paths:
                     client_id: "mailcow_client"
                     client_secret: "Xy7GdPqvJ9m3R8sT2LkVZ5W1oNbCaYQf"
                     redirect_url: "https://mail.mailcow.tld"
+                    redirect_url_extra: ["https://extramail.mailcow.tld"]
                     version: "26.1.3"
                     default_template: "Default"
                     mappers: ["small_mbox", "medium_mbox"]
@@ -6034,6 +6039,7 @@ paths:
                     client_id: "mailcow_client"
                     client_secret: "Xy7GdPqvJ9m3R8sT2LkVZ5W1oNbCaYQf"
                     redirect_url: "https://mail.mailcow.tld"
+                    redirect_url_extra: ["https://extramail.mailcow.tld"]
                     client_scopes: "openid profile email mailcow_template"
                     default_template: "Default"
                     mappers: ["small_mbox", "medium_mbox"]

+ 26 - 1
data/web/inc/functions.inc.php

@@ -2287,6 +2287,7 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
       $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
       foreach($rows as $row){
         switch ($row["key"]) {
+          case "redirect_url_extra":
           case "mappers":
           case "templates":
             $settings[$row["key"]] = json_decode($row["value"]);
@@ -2419,6 +2420,18 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
       }
       $pdo->commit();
 
+      // add redirect_url_extra
+      if (isset($_data['redirect_url_extra'])){
+        $_data['redirect_url_extra'] = (!is_array($_data['redirect_url_extra'])) ? array($_data['redirect_url_extra']) : $_data['redirect_url_extra'];
+
+        $redirect_url_extra = array_filter($_data['redirect_url_extra']);
+        $redirect_url_extra = json_encode($redirect_url_extra);
+
+        $stmt = $pdo->prepare("INSERT INTO identity_provider (`key`, `value`) VALUES ('redirect_url_extra', :value) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`);");
+        $stmt->bindParam(':value', $redirect_url_extra);
+        $stmt->execute();
+      }
+
       // add default template
       if (isset($_data['default_template'])) {
         $_data['default_template'] = (empty($_data['default_template'])) ? "" : $_data['default_template'];
@@ -2852,7 +2865,19 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
     case "get-redirect":
       if ($iam_settings['authsource'] != 'keycloak' && $iam_settings['authsource'] != 'generic-oidc')
         return false;
-      $authUrl = $iam_provider->getAuthorizationUrl();
+      $options = [];
+      if (isset($iam_settings['redirect_url_extra'])) {
+        // check if the current domain is used in an extra redirect URL
+        $targetDomain = strtolower($_SERVER['HTTP_HOST']);
+        foreach ($iam_settings['redirect_url_extra'] as $testUrl) {
+          $testUrlParsed = parse_url($testUrl);
+          if (isset($testUrlParsed['host']) && strtolower($testUrlParsed['host']) == $targetDomain) {
+            $options['redirect_uri'] = $testUrl;
+            break;
+          }
+        }
+      }
+      $authUrl = $iam_provider->getAuthorizationUrl($options);
       $_SESSION['oauth2state'] = $iam_provider->getState();
       return $authUrl;
     break;

+ 30 - 0
data/web/js/site/admin.js

@@ -789,6 +789,18 @@ jQuery(function($){
   $('.iam_ldap_rolemap_del').click(async function(e){
     deleteAttributeMappingRow(this, e);
   });
+  $('.iam_redirect_add_keycloak').click(async function(e){
+    addRedirectUrlRow('#iam_keycloak_redirect_list', '.iam_keycloak_redirect_del', e);
+  });
+  $('.iam_redirect_add_generic').click(async function(e){
+    addRedirectUrlRow('#iam_generic_redirect_list', '.iam_generic_redirect_del', e);
+  });
+  $('.iam_keycloak_redirect_del').click(async function(e){
+    deleteRedirectUrlRow(this, e);
+  });
+  $('.iam_generic_redirect_del').click(async function(e){
+    deleteRedirectUrlRow(this, e);
+  });
   // selecting identity provider
   $('#iam_provider').on('change', function(){
     // toggle password fields
@@ -833,4 +845,22 @@ jQuery(function($){
     if ($(elem).parent().parent().parent().parent().children().length > 1)
       $(elem).parent().parent().parent().remove();
   }
+  function addRedirectUrlRow(list_id, del_class, e) {
+    e.preventDefault();
+
+    var parent = $(list_id)
+    $(parent).children().last().clone().appendTo(parent);
+    var newChild = $(parent).children().last();
+    $(newChild).find('input').val('');
+
+    $(del_class).off('click');
+    $(del_class).click(async function(e){
+      deleteRedirectUrlRow(this, e);
+    });
+  }
+  function deleteRedirectUrlRow(elem, e) {
+    e.preventDefault();
+    if ($(elem).parent().parent().parent().parent().children().length > 2)
+      $(elem).parent().parent().parent().remove();
+  }
 });

+ 68 - 4
data/web/templates/admin/tab-config-identity-provider.twig

@@ -64,10 +64,42 @@
           </div>
           <div class="row mb-2">
             <div class="col-md-3 d-flex align-items-center justify-content-md-end">
-              <label class="control-label" for="iam_keycloak_redirecturl">{{ lang.admin.iam_redirect_url }}:</label>
+              <label class="control-label">{{ lang.admin.iam_redirect_url }}:</label>
             </div>
             <div class="col-12 col-md-9 col-lg-4">
-              <input type="text" class="form-control" id="iam_keycloak_redirecturl" name="redirect_url" value="{{ iam_settings.redirect_url }}" required>
+              <div class="row px-2 align-items-center">
+                <span class="col-10 p-0 pe-2">
+                  <input type="text" class="form-control"  name="redirect_url" value="{{ iam_settings.redirect_url }}" required>
+                </span>
+                <div class="col-2 p-0 d-flex">
+                  <button class="btn btn-sm d-block d-sm-inline btn-secondary ms-auto iam_redirect_add_keycloak"><i class="bi bi-plus-lg"></i></button>
+                </div>
+              </div>
+            </div>
+          </div>
+          <div class="row mb-2" id="iam_keycloak_redirect_list">
+            <input type="hidden" name="redirect_url_extra" value="">
+            {% for key, url in iam_settings.redirect_url_extra %}
+            <div class="offset-md-3 col-12 col-md-9 col-lg-4 mb-2">
+              <div class="row px-2">
+                <div class="col-10 p-0 pe-2">
+                  <input type="text" class="form-control me-2" name="redirect_url_extra" value="{{ iam_settings.redirect_url_extra[key] }}">
+                </div>
+                <div class="col-2 p-0 d-flex">
+                  <button class="iam_keycloak_redirect_del btn btn-sm d-block d-sm-inline btn-secondary ms-auto"><i class="bi bi-x-lg"></i></button>
+                </div>
+              </div>
+            </div>
+            {% endfor %}
+            <div class="offset-md-3 col-12 col-md-9 col-lg-4 mb-2">
+              <div class="row px-2">
+                <div class="col-10 p-0 pe-2">
+                  <input type="text" class="form-control me-2" name="redirect_url_extra" value="">
+                </div>
+                <div class="col-2 p-0 d-flex">
+                  <button class="iam_keycloak_redirect_del btn btn-sm d-block d-sm-inline btn-secondary ms-auto"><i class="bi bi-x-lg"></i></button>
+                </div>
+              </div>
             </div>
           </div>
           <div class="row mb-4">
@@ -274,10 +306,42 @@
           </div>
           <div class="row mb-2">
             <div class="col-md-3 d-flex align-items-center justify-content-md-end">
-              <label class="control-label" for="iam_redirect_url">{{ lang.admin.iam_redirect_url }}:</label>
+              <label class="control-label">{{ lang.admin.iam_redirect_url }}:</label>
             </div>
             <div class="col-12 col-md-9 col-lg-4">
-              <input type="text" class="form-control" id="iam_redirect_url" name="redirect_url" value="{{ iam_settings.redirect_url }}" required>
+              <div class="row px-2 align-items-center">
+                <span class="col-10 p-0 pe-2">
+                  <input type="text" class="form-control" name="redirect_url" value="{{ iam_settings.redirect_url }}" required>
+                </span>
+                <div class="col-2 p-0 d-flex">
+                  <button class="btn btn-sm d-block d-sm-inline btn-secondary ms-auto iam_redirect_add_generic"><i class="bi bi-plus-lg"></i></button>
+                </div>
+              </div>
+            </div>
+          </div>
+          <div class="row mb-2" id="iam_generic_redirect_list">
+            <input type="hidden" name="redirect_url_extra" value="">
+            {% for key, url in iam_settings.redirect_url_extra %}
+            <div class="offset-md-3 col-12 col-md-9 col-lg-4 mb-2">
+              <div class="row px-2">
+                <div class="col-10 p-0 pe-2">
+                  <input type="text" class="form-control me-2" name="redirect_url_extra" value="{{ iam_settings.redirect_url_extra[key] }}">
+                </div>
+                <div class="col-2 p-0 d-flex">
+                  <button class="iam_generic_redirect_del btn btn-sm d-block d-sm-inline btn-secondary ms-auto"><i class="bi bi-x-lg"></i></button>
+                </div>
+              </div>
+            </div>
+            {% endfor %}
+            <div class="offset-md-3 col-12 col-md-9 col-lg-4 mb-2">
+              <div class="row px-2">
+                <div class="col-10 p-0 pe-2">
+                  <input type="text" class="form-control me-2" name="redirect_url_extra" value="">
+                </div>
+                <div class="col-2 p-0 d-flex">
+                  <button class="iam_generic_redirect_del btn btn-sm d-block d-sm-inline btn-secondary ms-auto"><i class="bi bi-x-lg"></i></button>
+                </div>
+              </div>
             </div>
           </div>
           <div class="row mb-4">