using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using IdentityServer4; using IdentityServer4.Configuration; using IdentityServer4.Models; using IdentityServer4.Stores; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection.Infrastructure; using Microsoft.AspNetCore.DataProtection.KeyManagement; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace IdentityServerHost { [Authorize] public class MigrationAnalysisController : Controller { private readonly IServiceProvider _services; private readonly ILogger _logger; private readonly List _clients; public MigrationAnalysisController( IServiceProvider services, ILogger logger, IEnumerable clients) { _services = services; _logger = logger; _clients = clients.ToList(); // If not in-memory, attempt to resolve clients via known EntityFramework Core store if (_clients.Count == 0) { try { var configurationDbContextType = Type.GetType("IdentityServer4.EntityFramework.Interfaces.IConfigurationDbContext, IdentityServer4.EntityFramework.Storage"); if (configurationDbContextType != null) { var configurationDbContext = _services.GetRequiredService(configurationDbContextType); var clientsProperty = configurationDbContextType.GetProperty("Clients"); var clientsEntities = clientsProperty.GetValue(configurationDbContext); var clientMappersType = Type.GetType("IdentityServer4.EntityFramework.Mappers.ClientMappers, IdentityServer4.EntityFramework.Storage"); var toModelMethod = clientMappersType.GetMethod("ToModel"); _clients.Clear(); foreach (var client in (System.Collections.IEnumerable)clientsEntities) { var clientModel = (Client)toModelMethod.Invoke(null, new[] { client }); _clients.Add(clientModel); } } } catch (Exception e) { _logger.LogError(e, "Failed to load clients from EntityFramework store"); } } // No clients? if (_clients.Count == 0) { _logger.LogWarning("No in-memory or EntityFramework Core-based clients found in your IdentityServer4 setup."); } } public async Task Index() { // Verify user is allowed to access this page if (User.Identity == null || User.Identity.Name != "scott") { return Unauthorized(); } var table = new StringBuilder(); var jsonData = new List>(); table.AppendLine(""); table.AppendLine(HeaderRow("Description", "Raw Data", "Recommendation")); table.AppendLine(""); // .NET version var dotNetClrVersion = Environment.Version; var dotNetClrRecommendation = ""; if (dotNetClrVersion.Major < 10) { dotNetClrRecommendation = "Upgrade to latest .NET LTS version."; } table.AppendLine(DataRow(".NET version", dotNetClrVersion.ToString(), dotNetClrRecommendation)); jsonData.Add(new KeyValuePair("dotNetClrVersion", dotNetClrVersion.ToString())); // IdentityServer4 version var identityServerVersion = typeof(IdentityServerConstants).Assembly.GetName().Version; string identityServerRecommendation; if (identityServerVersion.Major < 4) { identityServerRecommendation = "Upgrade to IdentityServer4 4.x will be needed prior to being able to upgrade to Duende IdentityServer, as outlined at IdentityServer4 v3.x to v4.x."; } else { identityServerRecommendation = "Migration to Duende.IdentityServer can be done as described here IdentityServer4 v4.x to Duende IdentityServer."; } table.AppendLine(DataRow("IdentityServer4 version", identityServerVersion.ToString(), identityServerRecommendation)); jsonData.Add(new KeyValuePair("identityServerVersion", identityServerVersion.ToString())); // Clients var interactiveClientsCount = _clients.Count(c => c.AllowedGrantTypes.Contains(GrantType.AuthorizationCode)); var otherClientsCount = _clients.Count() - interactiveClientsCount; var clientData = string.Format("Interactive: {0}, Non-interactive: {1}", interactiveClientsCount, otherClientsCount); var clientDataRecommendation = "Determine the correct production environment licensing needs. See Duende IdentityServer Pricing."; table.AppendLine(DataRow("Number of interactive and non-interactive clients", clientData, clientDataRecommendation)); jsonData.Add(new KeyValuePair("clientCount", _clients.Count.ToString())); jsonData.Add(new KeyValuePair("clientData", clientData)); // Issuer URI var identityServerOptions = _services.GetRequiredService>(); var issuerUri = !string.IsNullOrEmpty(identityServerOptions.Value.IssuerUri) ? identityServerOptions.Value.IssuerUri : "(inferred from request)"; table.AppendLine(DataRow("Issuer URI", issuerUri, "")); jsonData.Add(new KeyValuePair("issuerUri", issuerUri)); // Signing credential store type var signingCredentialStore = _services.GetService(); var signingCredentialStoreType = signingCredentialStore?.GetType().FullName ?? "(not set)"; var signingCredentialStoreRecommendation = ""; if (signingCredentialStoreType != "(not set)" && !signingCredentialStoreType.StartsWith("IdentityServer4")) { signingCredentialStoreRecommendation = "Investigate compatibility."; } table.AppendLine(DataRow("Signing credential store type", signingCredentialStoreType, signingCredentialStoreRecommendation)); jsonData.Add(new KeyValuePair("signingCredentialStoreType", signingCredentialStoreType)); // Signing credential key id var signingCredentialKeyId = "(not set)"; if (signingCredentialStore != null) { var signingCredentials = await signingCredentialStore.GetSigningCredentialsAsync(); if (signingCredentials != null) { signingCredentialKeyId = signingCredentials.Kid; } } var signingCredentialKeyRecommendation = "Migrate Signing Keys (optional)"; table.AppendLine(DataRow("Signing credential key id", signingCredentialKeyId, signingCredentialKeyRecommendation)); jsonData.Add(new KeyValuePair("signingCredentialKeyId", signingCredentialKeyId)); // Data protection application name var dataProtectionApplicationDiscriminator = "(not set)"; var dataProtectionApplicationDiscriminatorService = _services.GetService(); if (dataProtectionApplicationDiscriminatorService != null) { dataProtectionApplicationDiscriminator = dataProtectionApplicationDiscriminatorService.Discriminator; } var dataProtectionOptions = _services.GetService>(); if (dataProtectionOptions != null && dataProtectionOptions.Value.ApplicationDiscriminator != null) { dataProtectionApplicationDiscriminator = dataProtectionOptions.Value.ApplicationDiscriminator; } var dataProtectionApplicationDiscriminatorRecommendation = ""; if (dataProtectionApplicationDiscriminator == "(not set)" || string.IsNullOrEmpty(dataProtectionApplicationDiscriminator) || dataProtectionApplicationDiscriminator.Contains("/") || dataProtectionApplicationDiscriminator.Contains("\\")) { dataProtectionApplicationDiscriminatorRecommendation = "Verify Data Protection Configuration"; } table.AppendLine(DataRow("Data protection application name", dataProtectionApplicationDiscriminator, dataProtectionApplicationDiscriminatorRecommendation)); jsonData.Add(new KeyValuePair("dataProtectionApplicationDiscriminator", dataProtectionApplicationDiscriminator)); // Data protection repository type var dataProtectionRepositoryType = "(not set)"; var dataProtectionKeyManagementOptions = _services.GetService>(); if (dataProtectionKeyManagementOptions != null && dataProtectionKeyManagementOptions.Value.XmlRepository != null) { dataProtectionRepositoryType = dataProtectionKeyManagementOptions.Value.XmlRepository.GetType().FullName; } var dataProtectionRepositoryTypeRecommendation = ""; if (dataProtectionRepositoryType == "(not set)") { dataProtectionRepositoryTypeRecommendation = "Consider a persistent store as per Microsoft documentation."; } table.AppendLine(DataRow("Data protection repository type", dataProtectionRepositoryType, dataProtectionRepositoryTypeRecommendation)); jsonData.Add(new KeyValuePair("dataProtectionRepositoryType", dataProtectionRepositoryType)); // Authentication schemes var authenticationSchemeProvider = _services.GetService(); if (authenticationSchemeProvider != null) { var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync(); foreach (var scheme in authenticationSchemes) { var schemeRecommendation = ""; if (!scheme.HandlerType.FullName.StartsWith("IdentityServer4") && !scheme.HandlerType.FullName.StartsWith("Microsoft")) { schemeRecommendation = "Research compatibility with ASP.NET Core and Duende IdentityServer."; } table.AppendLine(DataRow(string.Format("Authentication scheme: {0} {1}", scheme.Name, scheme.DisplayName), scheme.HandlerType.FullName, schemeRecommendation)); jsonData.Add(new KeyValuePair(string.Format("authenticationScheme:{0}", scheme.Name), scheme.HandlerType.FullName)); } } table.AppendLine(""); table.AppendLine("
"); // Build JSON var json = new StringBuilder(); json.AppendLine("{"); for (var i = 0; i < jsonData.Count; i++) { json.Append(JsonData(jsonData[i].Key, jsonData[i].Value)); json.AppendLine(i < jsonData.Count - 1 ? "," : ""); } json.AppendLine("}"); // language=html var html = string.Format(@" Duende Software - IdentityServer4 Migration Analysis
This report analyzes your current IdentityServer4 deployment and provides recommendations for migrating to Duende IdentityServer.
Note that the data provided is informative and should not be considered a complete migration plan.
{0}

Book a free IdentityServer4 Analysis call

", table.ToString(), json.ToString()); return Content(html, "text/html"); } private string HeaderRow(string col1, string col2, string col3) { return string.Format("{0}{1}{2}", col1, col2, col3); } private string DataRow(string col1, string col2, string col3) { return string.Format("{0}{1}{2}", col1, col2, col3); } private string JsonData(string property, string value) { return string.Format("\"{0}\": \"{1}\"", property, value.Replace("\"", "'")); } } }