Support env-based signing certificate

This commit is contained in:
Alexa
2026-06-30 13:54:56 +03:00
parent 71df7a3ef1
commit c50b305786
3 changed files with 34 additions and 5 deletions
@@ -32,12 +32,26 @@ public sealed class IdentityServerSigningKeyProvider
"JWT secret not configured. Set DORKNET_JWT_SECRET env var or Jwt:Secret in appsettings.Local.json.");
CertificatePath = ResolveCertificatePath(config);
Certificate = LoadOrCreateCertificate(CertificatePath, config, _legacySecret);
var password = config["IdentityServer:SigningCertificatePassword"] ?? _legacySecret;
var certificateBase64 = config["IdentityServer:SigningCertificateBase64"];
if (!string.IsNullOrWhiteSpace(certificateBase64))
{
LoadedFromBase64 = true;
Certificate = LoadCertificateFromBase64(certificateBase64, password);
}
else
{
Certificate = LoadOrCreateCertificate(CertificatePath, password);
}
SigningKey = new X509SecurityKey(Certificate);
LegacyJwtKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_legacySecret));
}
public string CertificatePath { get; }
public bool LoadedFromBase64 { get; }
public string CertificateSource => LoadedFromBase64
? "IdentityServer:SigningCertificateBase64"
: CertificatePath;
public X509Certificate2 Certificate { get; }
public SecurityKey SigningKey { get; }
public SecurityKey LegacyJwtKey { get; }
@@ -51,9 +65,16 @@ public sealed class IdentityServerSigningKeyProvider
return path;
}
private static X509Certificate2 LoadOrCreateCertificate(string path, IConfiguration config, string fallbackPassword)
private static X509Certificate2 LoadCertificateFromBase64(string certificateBase64, string password)
{
var flags = X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.Exportable;
var compact = new string(certificateBase64.Where(c => !char.IsWhiteSpace(c)).ToArray());
var bytes = Convert.FromBase64String(compact);
return new X509Certificate2(bytes, password, flags);
}
private static X509Certificate2 LoadOrCreateCertificate(string path, string password)
{
var password = config["IdentityServer:SigningCertificatePassword"] ?? fallbackPassword;
var flags = X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.Exportable;
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
@@ -1169,6 +1169,7 @@ public class AdminController(
var client = RecRoomIdentityServerConfig.Clients[0];
var certPath = signingKeys.CertificatePath;
var fullCertPath = Path.GetFullPath(certPath);
var certificateExists = signingKeys.LoadedFromBase64 || System.IO.File.Exists(fullCertPath);
var licenseConfigured =
!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("DUENDE_IDENTITYSERVER_LICENSE_KEY"))
|| !string.IsNullOrWhiteSpace(config["IdentityServer:LicenseKey"]);
@@ -1194,8 +1195,10 @@ public class AdminController(
},
Signing = new
{
signingKeys.CertificateSource,
signingKeys.LoadedFromBase64,
CertificatePath = fullCertPath,
CertificateExists = System.IO.File.Exists(fullCertPath),
CertificateExists = certificateExists,
signingKeys.Certificate.Thumbprint,
NotBeforeUtc = signingKeys.Certificate.NotBefore.ToUniversalTime(),
NotAfterUtc = signingKeys.Certificate.NotAfter.ToUniversalTime(),
@@ -20,6 +20,8 @@ interface IdentityServerSettings {
refreshTokenExpiration: string;
};
signing: {
certificateSource: string;
loadedFromBase64: boolean;
certificatePath: string;
certificateExists: boolean;
thumbprint: string;
@@ -139,7 +141,10 @@ export function IdentityServer() {
<div className="card !p-5">
<h2 className="text-sm font-semibold text-ink-50">Signing certificate</h2>
<div className="mt-4 space-y-3">
<Detail label="Path" value={settings.signing.certificatePath} mono />
<Detail label="Source" value={settings.signing.certificateSource} mono />
{!settings.signing.loadedFromBase64 && (
<Detail label="Path" value={settings.signing.certificatePath} mono />
)}
<Detail label="Thumbprint" value={settings.signing.thumbprint} mono />
<Detail label="Algorithm" value={settings.signing.algorithm} mono />
<Detail label="Valid from" value={formatDate(settings.signing.notBeforeUtc)} />