diff --git a/data/web/api/openapi.yaml b/data/web/api/openapi.yaml index afab5fb0c..f207ee6a1 100644 --- a/data/web/api/openapi.yaml +++ b/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"] diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 49e26b978..5f9c772d0 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -2286,6 +2286,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"]); @@ -2418,6 +2419,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']; @@ -2851,7 +2864,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; diff --git a/data/web/js/site/admin.js b/data/web/js/site/admin.js index b6edf6f80..cf1efd823 100644 --- a/data/web/js/site/admin.js +++ b/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(); + } }); diff --git a/data/web/templates/admin/tab-config-identity-provider.twig b/data/web/templates/admin/tab-config-identity-provider.twig index dce26f8c3..a93002257 100644 --- a/data/web/templates/admin/tab-config-identity-provider.twig +++ b/data/web/templates/admin/tab-config-identity-provider.twig @@ -64,10 +64,42 @@