/*
* dbMango
*
* Copyright 2025 Deutsche Bank AG
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.Reflection;
namespace Rms.Service.Bootstrap.Security;
///
/// OpenIdConnect configuration loader
///
internal static class OidcConfiguration
{
private static readonly ILogger _log = Logging.Logging.GetLogger(MethodBase.GetCurrentMethod()?.DeclaringType ?? typeof(int));
private static readonly Lock _lock = new();
///
/// /.well-known/openid-configuration
///
private const string ConfigurationSegment = "/.well-known/openid-configuration";
///
/// Loaded Oidc configuration to use
///
internal static OpenIdConnectConfiguration? Configuration { get; private set; }
public static OpenIdConnectConfiguration Load(SecuritySettings settings)
{
if ( Configuration != null )
return Configuration;
lock ( _lock )
{
if ( Configuration != null )
return Configuration;
LoadOidcConfigurationInternal(settings);
return Configuration!;
}
}
private static void LoadOidcConfigurationInternal(SecuritySettings settings)
{
var url = settings.Oidc.ConfigUrl + ConfigurationSegment;
try
{
_log.LogTrace($"Loading Oidc configuration from {url}");
var configManager = new ConfigurationManager(url, new OpenIdConnectConfigurationRetriever());
Configuration = configManager.GetConfigurationAsync().Result;
_log.LogDebug($"Oidc configuration loaded from {url}");
SaveOidcConfigurationToCache(settings);
}
catch (Exception e)
{
_log.LogDebug($"Can't load Oidc configuration from {url}\n{e}");
var cacheFile = GetCacheFileName(settings);
if ( settings.Oidc.ConfigCacheFile == null || !File.Exists( cacheFile))
throw new ApplicationException($"Can't load Oidc configuration from {url}", e);
var json = File.ReadAllText(cacheFile);
Configuration = new(json);
_log.LogWarning($"Oidc configuration loaded from {cacheFile}");
}
}
private static void SaveOidcConfigurationToCache(SecuritySettings settings)
{
var cacheFile = GetCacheFileName(settings);
if ( cacheFile == null )
return;
try
{
var json = OpenIdConnectConfiguration.Write(Configuration);
var tmpName = cacheFile + ".tmp";
if ( File.Exists(tmpName) )
File.Delete(tmpName);
File.WriteAllText(tmpName, json);
if ( File.Exists(cacheFile) )
File.Delete(cacheFile);
File.Move(tmpName, cacheFile);
_log.LogDebug($"Oidc configuration cached into {cacheFile}");
}
catch (Exception ex)
{
_log.LogWarning($"Oidc configuration can't be saved to {cacheFile}: {ex.Message}");
}
}
private static string? GetCacheFileName(SecuritySettings settings)
{
var cacheFile = settings.Oidc.ConfigCacheFile;
if (cacheFile == null)
return cacheFile;
cacheFile = Environment.ExpandEnvironmentVariables(cacheFile);
if (cacheFile.Contains('%'))
cacheFile = Path.Combine(Path.GetTempPath(), ".oidc.settings.json");
settings.Oidc.ConfigCacheFile = cacheFile;
return cacheFile;
}
}