It looks there is a bug here as where these properties are stored looks to be an array, but everytime you restart the application it overwrites the array with one URL
[dbo].[umbracoOpenIddictApplications]
PostLogoutRedirectUris
RedirectUris
As a work around to get this working for now we have added this service to the program.cs to correct the two fields above, and allow us to share a dev database between multiple URLs
using System.Data;
using System.Text.Json;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
public class OpenIdUpdateService(IServiceProvider serviceProvider, ILogger<OpenIdUpdateService> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Ensure the application is fully started before running SQL
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); // Small delay for safety
using (var scope = serviceProvider.CreateScope())
{
var configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
string connectionString = configuration?.GetConnectionString("UmbracoDbDSN") ?? string.Empty;
if (string.IsNullOrEmpty(connectionString))
{
logger.LogError("[OpenIdUpdateService] Database connection string is missing.");
return;
}
// Get values from appsettings.json
var postLogoutRedirectUris = configuration.GetSection("OpenIdSettings:PostLogoutRedirectUris").Get<List<string>>();
var redirectUris = configuration.GetSection("OpenIdSettings:RedirectUris").Get<List<string>>();
if (postLogoutRedirectUris == null || redirectUris == null)
{
logger.LogError("[OpenIdUpdateService] OpenID settings are missing in appsettings.json.");
return;
}
// Convert lists to JSON format for SQL
string postLogoutRedirectUrisJson = JsonSerializer.Serialize(postLogoutRedirectUris);
string redirectUrisJson = JsonSerializer.Serialize(redirectUris);
// Corrected SQL statement with proper JSON formatting
string sql = @"
UPDATE [dbo].[umbracoOpenIddictApplications]
SET PostLogoutRedirectUris = @PostLogoutRedirectUris,
RedirectUris = @RedirectUris
WHERE ClientId = 'umbraco-back-office';
";
try
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
await conn.OpenAsync(stoppingToken);
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("@PostLogoutRedirectUris", postLogoutRedirectUrisJson);
cmd.Parameters.AddWithValue("@RedirectUris", redirectUrisJson);
int rowsAffected = await cmd.ExecuteNonQueryAsync(stoppingToken);
logger.LogInformation($"[OpenIdUpdateService] Database update successful. Rows affected: {rowsAffected}");
}
}
}
catch (Exception ex)
{
logger.LogError($"[OpenIdUpdateService] Error updating database: {ex.Message}");
}
}
}
}
register it in program.cs
// Register OpenIdUpdateService to run after startup
// This is a temporary fix (hack) to resolve an issue in Umbraco 15 overwriting the OpenId return URLs
builder.Services.AddHostedService<OpenIdUpdateService>();
Add all the URLs you wish to whitelist to appsettings.json
"OpenIdSettings": {
"PostLogoutRedirectUris": [
"https://dev.YOURSITE.com/umbraco/oauth_complete",
"https://dev.YOURSITE.com/umbraco/logout",
"https://YOURSITE.local/umbraco/oauth_complete",
"https://YOURSITE.local/umbraco/logout",
"https://YOURSITE-development.azurewebsites.net/umbraco/oauth_complete",
"https://YOURSITE-development.azurewebsites.net/umbraco/logout"
],
"RedirectUris": [
"https://dev.YOURSITE.com/umbraco/oauth_complete",
"https://YOURSITE.local/umbraco/oauth_complete",
"https://YOURSITE-development.azurewebsites.net/umbraco/oauth_complete"
]
}
This all feels very hacky, but it got us up and running again so hope it helps someone else until we figure out a neater way