Skip to content

Allow additional domains in OAuth2 redirect URLs #6483

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions data/web/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"]
Expand Down Expand Up @@ -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"]
Expand Down
27 changes: 26 additions & 1 deletion data/web/inc/functions.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -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"]);
Expand Down Expand Up @@ -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'];
Expand Down Expand Up @@ -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;
Expand Down
30 changes: 30 additions & 0 deletions data/web/js/site/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
});
72 changes: 68 additions & 4 deletions data/web/templates/admin/tab-config-identity-provider.twig
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down Expand Up @@ -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">
Expand Down