Ends in:
2 DAYS
17 HRS
46 MIN
19 SEC
Ends in:
2 D
17 H
46 M
19 S
Open Closed

Migrating from IdentityServer to OpenIddict. CurrentUser stays empty. #6324


User avatar
0
JanneHarju created
  • ABP Framework version: v7.3.2
  • UI Type: Angular
  • Database System: EF Core (SQL Server)
  • Tiered (for MVC) or Auth Server Separated (for Angular): Auth Server Separated
  • Exception message and full stack trace:

I'm migrating our application from IdentityServer to OpenIddict. Our application is running at Azure. When running locally all is working as it should. When running in Azure, login can be done succesfully and in browser's network tab I can see that all tokens are got succesfully. After authserver token call there is /api/abp/application-configuration?includeLocalizationResources=false call which returns status code 200 but currenUser and auth.grantedPolicies are empty. When checking logs there is line saying that ProcessAuthenticationContext was marked as rejected by OpenIddict.Validation.OpenIddict. I have done these migration guides: https://docs.abp.io/en/abp/latest/Migration-Guides/OpenIddict-Step-by-Step and https://docs.abp.io/en/abp/latest/Migration-Guides/OpenIddict-Angular I added that app.UseAbpOpenIddictValidation(); to our AuthServer. And now I notice that it was not needed, only if authserver is part of host, but our AuthServer is separeted. So now I removed it from AuthServer even though in example project what I generated there is UseAbpOpenIddictValidation added. After I removed I don't get anymore that rejection log line but it still doesn't work. When I did migration I left IdentityServer tables in database if I need to check some configurations or if I need backup plan. Eventhough there were not anything about scope update I changed role to roles in fronend configuration because in OpenIddictDataSeedContributor I notice that role was changed from role to roles.

Do I need to add some configuration or code to backend to get it work?


24 Answer(s)
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    Could you share the full logs?

  • User Avatar
    0
    JanneHarju created

    I didn't fine away to add log file here. Here is Backend/Host logs in csv format: "timestamp [UTC]",message,severityLevel,itemType,customDimensions,"operation_Id","operation_ParentId","cloud_RoleName" "12/13/2023, 8:48:48.991 AM","Executing AbpApplicationConfigurationAppService.GetAsync()...",0,trace,"{""SourceContext"":""Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationAppService"",""MessageTemplate"":""Executing AbpApplicationConfigurationAppService.GetAsync()..."",""RequestPath"":""/api/abp/application-configuration"",""ActionName"":""Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController.GetAsync (Volo.Abp.AspNetCore.Mvc)"",""ActionId"":""a0f9bcf3-0f25-4dd5-8702-e3a09b0f64a0"",""RequestId"":""40003582-0001-f300-b63f-84710c7967bb"",""Application"":""SCM.HttpApi.Host - TEST"",""CorrelationId"":""d99a011a94a34383bd1bbe6730eab4c8""}",4727d2b2f014411fb618834cf591ee00,b42bde1906fe903b,"SCM.HttpApi.Host" "12/13/2023, 8:48:49.168 AM","Executed AbpApplicationConfigurationAppService.GetAsync().",0,trace,"{""SourceContext"":""Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationAppService"",""MessageTemplate"":""Executed AbpApplicationConfigurationAppService.GetAsync()."",""RequestPath"":""/api/abp/application-configuration"",""ActionName"":""Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController.GetAsync (Volo.Abp.AspNetCore.Mvc)"",""ActionId"":""a0f9bcf3-0f25-4dd5-8702-e3a09b0f64a0"",""RequestId"":""40003582-0001-f300-b63f-84710c7967bb"",""Application"":""SCM.HttpApi.Host - TEST"",""CorrelationId"":""d99a011a94a34383bd1bbe6730eab4c8""}",4727d2b2f014411fb618834cf591ee00,b42bde1906fe903b,"SCM.HttpApi.Host" "12/13/2023, 8:48:54.649 AM","Executing AbpApplicationConfigurationAppService.GetAsync()...",0,trace,"{""SourceContext"":""Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationAppService"",""MessageTemplate"":""Executing AbpApplicationConfigurationAppService.GetAsync()..."",""RequestPath"":""/api/abp/application-configuration"",""ActionName"":""Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController.GetAsync (Volo.Abp.AspNetCore.Mvc)"",""ActionId"":""a0f9bcf3-0f25-4dd5-8702-e3a09b0f64a0"",""RequestId"":""4000358d-0001-f300-b63f-84710c7967bb"",""Application"":""SCM.HttpApi.Host - TEST"",""CorrelationId"":""63b25051809047248922748ff3bd5e95""}",cbe749ecf65a4e93b2120648cf926db3,f5e601b5d8902d99,"SCM.HttpApi.Host" "12/13/2023, 8:48:54.797 AM","Executed AbpApplicationConfigurationAppService.GetAsync().",0,trace,"{""SourceContext"":""Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationAppService"",""MessageTemplate"":""Executed AbpApplicationConfigurationAppService.GetAsync()."",""RequestPath"":""/api/abp/application-configuration"",""ActionName"":""Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController.GetAsync (Volo.Abp.AspNetCore.Mvc)"",""ActionId"":""a0f9bcf3-0f25-4dd5-8702-e3a09b0f64a0"",""RequestId"":""4000358d-0001-f300-b63f-84710c7967bb"",""Application"":""SCM.HttpApi.Host - TEST"",""CorrelationId"":""63b25051809047248922748ff3bd5e95""}",cbe749ecf65a4e93b2120648cf926db3,f5e601b5d8902d99,"SCM.HttpApi.Host" "12/13/2023, 8:49:07.739 AM","Executing AbpApplicationConfigurationAppService.GetAsync()...",0,trace,"{""SourceContext"":""Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationAppService"",""MessageTemplate"":""Executing AbpApplicationConfigurationAppService.GetAsync()..."",""RequestPath"":""/api/abp/application-configuration"",""ActionName"":""Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController.GetAsync (Volo.Abp.AspNetCore.Mvc)"",""ActionId"":""a0f9bcf3-0f25-4dd5-8702-e3a09b0f64a0"",""RequestId"":""400026de-0001-e100-b63f-84710c7967bb"",""Application"":""SCM.HttpApi.Host - TEST"",""CorrelationId"":""8c97b7aad9ab49499f75dac371b7053e""}",e4e9bd5d0c604e7b89778d416591b063,dcb91bc875e77043,"SCM.HttpApi.Host" "12/13/2023, 8:49:07.906 AM","Executed AbpApplicationConfigurationAppService.GetAsync().",0,trace,"{""SourceContext"":""Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationAppService"",""MessageTemplate"":""Executed AbpApplicationConfigurationAppService.GetAsync()."",""RequestPath"":""/api/abp/application-configuration"",""ActionName"":""Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController.GetAsync (Volo.Abp.AspNetCore.Mvc)"",""ActionId"":""a0f9bcf3-0f25-4dd5-8702-e3a09b0f64a0"",""RequestId"":""400026de-0001-e100-b63f-84710c7967bb"",""Application"":""SCM.HttpApi.Host - TEST"",""CorrelationId"":""8c97b7aad9ab49499f75dac371b7053e""}",e4e9bd5d0c604e7b89778d416591b063,dcb91bc875e77043,"SCM.HttpApi.Host"

    AuthServer log are too big to send here. I tried to taken only sort part of log around my test login process, but there is so much other logging and I'm not sure which of them you are interested in. Is there any other way to share logs? Or should I add it in parts?

  • User Avatar
    0
    JanneHarju created

    Here is link to my OneDrive share: https://1drv.ms/u/s!AuMQ6BLw9_WR3BWAa3X-bGOqq3mD?e=fR2mAE Hopefully it work for you.

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    Sorry, I could not open the link. could you send log via email?

  • User Avatar
    0
    JanneHarju created

    Yes I can. What is your address? I didn't manage to find any profile page of yours.

  • User Avatar
    0
    JanneHarju created

    Lets try google drive. https://drive.google.com/file/d/1aP8ErxxvPPnMCxZ_707llRAQYWSRDGfS/view?usp=drive_link

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    I didn't see any ProcessAuthenticationContext was marked as rejected by OpenIddict.Validation.OpenIddict message in the logs.

    Could you share the full log file both Authserver and HttpAPI.Host? thanks.

  • User Avatar
    0
    JanneHarju created

    Sorry about my misleading description. I removed UseAbpOpenIddictValidation() from AuthServer so there is no more that rejection. After that removal currentUser stays empty although login was done succesfully. So it wasn't about token validation. So it is something else.

  • User Avatar
    0
    JanneHarju created

    Here is AuthServer module if it helps anything.

    using System;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Security.Claims;
    using System.Security.Cryptography.X509Certificates;
    using Localization.Resources.AbpUi;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Authentication.Google;
    using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
    using Microsoft.AspNetCore.Authentication.Twitter;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Cors;
    using Microsoft.AspNetCore.Extensions.DependencyInjection;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.HttpOverrides;
    using Microsoft.EntityFrameworkCore.Metadata.Internal;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.IdentityModel.Protocols.OpenIdConnect;
    using OpenIddict.Validation.AspNetCore;
    using ABC.Analytics.EntityFrameworkCore;
    using ABC.Application.EntityFrameworkCore;
    using ABC.ObjectControl;
    using ABC.ObjectControl.EntityFrameworkCore;
    using ABC.ObjectControl.Localization;
    using ABC.ObjectControl.MultiTenancy;
    using ABC.MasterDataManagement.EntityFrameworkCore;
    using ABC.Shared.Hosting;
    using Volo.Abp;
    using Volo.Abp.Account;
    using Volo.Abp.Account.Localization;
    using Volo.Abp.Account.Public.Web;
    using Volo.Abp.Account.Public.Web.ExternalProviders;
    using Volo.Abp.Account.Public.Web.Impersonation;
    using Volo.Abp.Account.Web;
    using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
    using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.Localization;
    using Volo.Abp.AspNetCore.Mvc.UI.Theme.Lepton;
    using Volo.Abp.AspNetCore.Mvc.UI.Theme.Lepton.Bundling;
    using Volo.Abp.AspNetCore.Serilog;
    using Volo.Abp.Auditing;
    using Volo.Abp.BackgroundJobs;
    using Volo.Abp.Caching.StackExchangeRedis;
    using Volo.Abp.Emailing;
    using Volo.Abp.Identity;
    using Volo.Abp.Localization;
    using Volo.Abp.Modularity;
    using Volo.Abp.OpenIddict;
    using Volo.Abp.OpenIddict.Localization;
    using Volo.Abp.UI.Navigation.Urls;
    using Volo.Abp.VirtualFileSystem;
    using Volo.Saas.Host;
    
    namespace ABC
    {
    	[DependsOn(
    		typeof(ABCSharedHostingModule),
    		typeof(AbpCachingStackExchangeRedisModule),
    		typeof(AbpAspNetCoreSerilogModule),
    		typeof(AbpAccountPublicWebOpenIddictModule),
    		typeof(AbpAccountPublicHttpApiModule),
    		typeof(AbpAspNetCoreMvcUiLeptonThemeModule),
    		typeof(AbpAccountPublicApplicationModule),
    		typeof(AbpAccountPublicWebImpersonationModule),
    		typeof(SaasHostApplicationContractsModule),
    		typeof(ABCEntityFrameworkCoreModule),
    		typeof(MasterDataManagementEntityFrameworkCoreModule),
    		typeof(AnalyticsEntityFrameworkCoreModule),
    		typeof(AbpEmailingModule),
    		// ABCApplicationEntityFrameworkCoreModule must be after the other EntityFrameworkCoreModules to replace db contexts
    		typeof(ABCApplicationEntityFrameworkCoreModule)
    	)]
    	public class ABCAuthServerModule : AbpModule
    	{
    		public override void PreConfigureServices(ServiceConfigurationContext context)
    		{
    			PreConfigure<OpenIddictBuilder>(builder =>
    			{
    				builder.AddValidation(options =>
    				{
    					options.AddAudiences("ABC");
    					options.UseLocalServer();
    					options.UseAspNetCore();
    				});
    			});
    			var hostingEnvironment = context.Services.GetHostingEnvironment();
    
    			// Other than development environments, use the certificates from the certificate store
    			if (!hostingEnvironment.IsDevelopment())
    			{
    				PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
    				{
    					options.AddDevelopmentEncryptionAndSigningCertificate = false;
    				});
    				var configuration = context.Services.GetConfiguration();
    				PreConfigure<OpenIddictServerBuilder>(builder =>
    				{
    					builder.AddSigningCertificate(LoadCertificate(configuration["AuthServer:SigningCertificateThumbprint"]));
    					builder.AddEncryptionCertificate(LoadCertificate(configuration["AuthServer:EncryptionCertificateThumbprint"]));
    				});
    			}
    		}
    		public override void ConfigureServices(ServiceConfigurationContext context)
    		{
    			if (context == null)
    			{
    				throw new ArgumentNullException(nameof(context));
    			}
    
    			// Enable for debugging only
    			Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = false;
    
    			var hostingEnvironment = context.Services.GetHostingEnvironment();
    			var configuration = context.Services.GetConfiguration();
    			Configure<EmailSenderConfiguration>(configuration.GetSection("Settings"));
    			Configure<ForwardedHeadersOptions>(options => options.ForwardedHeaders = ForwardedHeaders.XForwardedHost);
    			context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
    			// Add a custom CSS to your ABP MVC / Razor Pages solution
    			// https://docs.abp.io/en/commercial/7.1/themes%2Flepton%2Fcustomizing-lepton-theme?UI=MVC
    			Configure<LeptonThemeOptions>(options =>
    			{
    				options.StylePath = $"/Themes/Lepton/Global/Styles/lepton7.css";
    			});
    
    			Configure<AbpLocalizationOptions>(options =>
    			{
    				options.Resources
    					.Get<ABCObjectControlResource>()
    					.AddBaseTypes(
    						typeof(AbpUiResource)
    					);
    
    				options.Resources
    					.Get<AbpUiResource>()
    					.AddVirtualJson("/Localization/AbpUi");
    
    				options.Resources
    					.Get<AccountResource>()
    					.AddVirtualJson("/Localization/Account");
    
    				options.Resources
    					.Get<AbpOpenIddictResource>()
    					.AddVirtualJson("/Localization/OpenIddict");
    
    				options.Resources
    					.Get<AbpUiMultiTenancyResource>()
    					.AddVirtualJson("/Localization/AbpUiMultiTenancy");
    			});
    
    			Configure<AbpBundlingOptions>(options =>
    			{
    				options.StyleBundles.Configure(
    					LeptonThemeBundles.Styles.Global,
    					bundle => bundle.AddFiles("/global-styles.css")
    				);
    			});
    
    			Configure<AbpAuditingOptions>(options =>
    			{
    				options.IsEnabledForGetRequests = true;
    				options.ApplicationName = "AuthServer";
    			});
    
    			if (hostingEnvironment.IsDevelopment())
    			{
    				Configure<AbpVirtualFileSystemOptions>(options =>
    				{
    					options.FileSets.ReplaceEmbeddedByPhysical<ABCObjectControlDomainSharedModule>(
    						Path.Combine(hostingEnvironment.ContentRootPath,
    							String.Format("..{0}ABC.ObjectControl.Domain.Shared", Path.DirectorySeparatorChar)));
    					options.FileSets.ReplaceEmbeddedByPhysical<ABCObjectControlDomainModule>(
    						Path.Combine(hostingEnvironment.ContentRootPath,
    							String.Format("..{0}ABC.ObjectControl.Domain", Path.DirectorySeparatorChar)));
    				});
    			}
    
    			Configure<AppUrlOptions>(options =>
    			{
    				options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
    				options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(','));
    			});
    
    			Configure<AbpBackgroundJobOptions>(options => options.IsJobExecutionEnabled = false);
    
    			context.Services.AddCors(options =>
    			{
    				options.AddDefaultPolicy(builder =>
    				{
    					builder
    						.WithOrigins(
    							configuration["App:CorsOrigins"]
    								.Split(",", StringSplitOptions.RemoveEmptyEntries)
    								.Select(o => o.Trim().RemovePostFix("/"))
    								.ToArray()
    						)
    						.WithAbpExposedHeaders()
    						.SetIsOriginAllowedToAllowWildcardSubdomains()
    						.AllowAnyHeader()
    						.AllowAnyMethod()
    						.AllowCredentials();
    				});
    			});
    
    			var authenticationBuilder = context.Services.AddAuthentication();
    			if (configuration.GetValue<bool?>("AzureAd:IsEnbaled") is true)
    			{
    				authenticationBuilder.AddOpenIdConnect("AzureOpenId", "Azure AD", options =>
    				{
    					options.Authority = "https://login.microsoftonline.com/" + configuration["AzureAd:TenantId"] + "/v2.0/";
    					options.ClientId = configuration["AzureAd:ClientId"];
    					options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
    					options.CallbackPath = configuration["AzureAd:CallbackPath"];
    					options.ClientSecret = configuration["AzureAd:ClientSecret"];
    					options.RequireHttpsMetadata = false;
    					options.SaveTokens = true;
    					options.GetClaimsFromUserInfoEndpoint = true;
    					options.Scope.Add("email");
    
    					options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
    				});
    			}
    
    			authenticationBuilder.AddGoogle(GoogleDefaults.AuthenticationScheme, _ => { })
    			.WithDynamicOptions<GoogleOptions, GoogleHandler>(
    				GoogleDefaults.AuthenticationScheme,
    				options =>
    				{
    					options.WithProperty(x => x.ClientId);
    					options.WithProperty(x => x.ClientSecret, isSecret: true);
    				}
    			)
    			.AddMicrosoftAccount(MicrosoftAccountDefaults.AuthenticationScheme, options =>
    			{
    				//Personal Microsoft accounts as an example.
    				options.AuthorizationEndpoint =
    				"https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize";
    				options.TokenEndpoint = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token";
    			})
    			.WithDynamicOptions<MicrosoftAccountOptions, MicrosoftAccountHandler>(
    				MicrosoftAccountDefaults.AuthenticationScheme,
    				options =>
    				{
    					options.WithProperty(x => x.ClientId);
    					options.WithProperty(x => x.ClientSecret, isSecret: true);
    				}
    			)
    			.AddTwitter(TwitterDefaults.AuthenticationScheme,
    				options => options.RetrieveUserDetails = true)
    			.WithDynamicOptions<TwitterOptions, TwitterHandler>(
    				TwitterDefaults.AuthenticationScheme,
    				options =>
    				{
    					options.WithProperty(x => x.ConsumerKey);
    					options.WithProperty(x => x.ConsumerSecret, isSecret: true);
    				}
    			);
    
    			context.Services.Configure<AbpAccountOptions>(options =>
    			{
    				options.TenantAdminUserName = "admin";
    				options.ImpersonationTenantPermission = SaasHostPermissions.Tenants.Impersonation;
    				options.ImpersonationUserPermission = IdentityPermissions.Users.Impersonation;
    			});
    
    			ApplicationInsightsConfigurationHelper.AddApplicationInsightsForHttpApplications(context, configuration, Assembly.GetExecutingAssembly().GetName().Name);
    		}
    
    		public override void OnApplicationInitialization(ApplicationInitializationContext context)
    		{
    			var app = context.GetApplicationBuilder();
    			var env = context.GetEnvironment();
    
    			if (env.IsDevelopment())
    			{
    				app.UseDeveloperExceptionPage();
    			}
    
    			app.UseAbpRequestLocalization();
    
    
    #pragma warning disable S125 // Sections of code should not be commented out
    			/*
    				if (!env.IsDevelopment())
    				{
    			 		app.UseErrorPage();
    				}
    			 */
    #pragma warning restore S125 // Sections of code should not be commented out
    
    			app.UseForwardedHeaders();
    			app.UseCorrelationId();
    			app.UseStaticFiles();
    			app.UseRouting();
    			app.UseCors();
    			app.UseAuthentication();
    
    			if (MultiTenancyConsts.IS_ENABLED)
    			{
    				app.UseMultiTenancy();
    			}
    
    			app.UseUnitOfWork();
    			app.UseAuthorization();
    
    			app.UseAuditing();
    			app.UseAbpSerilogEnrichers();
    			app.UseConfiguredEndpoints();
    		}
    
    
    #pragma warning disable CA1822 // Method can be made static
    		private X509Certificate2 LoadCertificate(string thumbprint)
    #pragma warning restore CA1822 // Method can be made static
    		{
    			if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
    			{
    				using (X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
    				{
    					certStore.Open(OpenFlags.ReadOnly);
    
    					X509Certificate2Collection certCollection = certStore.Certificates.Find(
    												X509FindType.FindByThumbprint,
    												// Replace below with your certificate's thumbprint
    												thumbprint,
    												false);
    					// Get the first cert with the thumbprint
    					X509Certificate2 cert = certCollection.OfType<X509Certificate2>().FirstOrDefault();
    
    					if (cert is null)
    						throw new AbpInitializationException($"Certificate with thumbprint {thumbprint} was not found");
    
    					// Use certificate
    					Console.WriteLine(cert.FriendlyName);
    					return cert;
    				}
    			}
    			else if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
    			{
    				var bytes = File.ReadAllBytes($"/var/ssl/private/{thumbprint}.p12");
    				return new X509Certificate2(bytes);
    			}
    			else
    			{
    				throw new AbpInitializationException("Unsupported OS");
    			}
    			
    		}
    	}
    }
    	
    
  • User Avatar
    0
    JanneHarju created

    And here is our Host module.

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Security.Claims;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Authentication.Google;
    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
    using Microsoft.AspNetCore.Authentication.Twitter;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Cors;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.IdentityModel.Protocols.OpenIdConnect;
    using Microsoft.OpenApi.Models;
    using ABC.Analytics;
    using ABC.Analytics.EntityFrameworkCore;
    using ABC.Application.EntityFrameworkCore;
    using ABC.HealthChecks;
    using ABC.Integration;
    using ABC.Integration.EntityFrameworkCore;
    using ABC.ObjectControl;
    using ABC.ObjectControl.EntityFrameworkCore;
    using ABC.ObjectControl.MultiTenancy;
    using ABC.ItemPortfolioManagement;
    using ABC.ItemPortfolioManagement.EntityFrameworkCore;
    using ABC.MasterDataManagement;
    using ABC.MasterDataManagement.EntityFrameworkCore;
    using ABC.Procurement.EntityFrameworkCore;
    using ABC.Shared.BackgroundJobs;
    using ABC.Shared.Hosting;
    using Volo.Abp;
    using Volo.Abp.Account;
    using Volo.Abp.AspNetCore.Mvc;
    using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy;
    using Volo.Abp.AspNetCore.Serilog;
    using Volo.Abp.BackgroundJobs;
    using Volo.Abp.Caching.StackExchangeRedis;
    using Volo.Abp.Identity.AspNetCore;
    using Volo.Abp.Modularity;
    using Volo.Abp.Swashbuckle;
    using Volo.Abp.UI.Navigation.Urls;
    using Volo.Abp.VirtualFileSystem;
    
    namespace ABC
    {
    	[DependsOn(
    		typeof(ABCSharedHostingModule),
    		typeof(ABCSharedBackgroundJobsModule),
    		typeof(ABCObjectControlHttpApiModule),
    		typeof(AbpCachingStackExchangeRedisModule),
    		typeof(AbpAspNetCoreMvcUiMultiTenancyModule),
    		typeof(AbpIdentityAspNetCoreModule),
    		typeof(ABCObjectControlApplicationModule),
    		typeof(ABCEntityFrameworkCoreModule),
    		typeof(MasterDataManagementEntityFrameworkCoreModule),
    		typeof(AnalyticsEntityFrameworkCoreModule),
    		typeof(ProcurementEntityFrameworkCoreModule),
    		typeof(IntegrationEntityFrameworkCoreModule),
    		typeof(ItemPortfolioManagementEntityFrameworkCoreModule),
    		// ABCApplicationEntityFrameworkCoreModule must be after the other EntityFrameworkCoreModules to replace db contexts
    		typeof(ABCApplicationEntityFrameworkCoreModule),
    		typeof(AbpSwashbuckleModule),
    		typeof(AbpAspNetCoreSerilogModule),
    		typeof(IntegrationApplicationModule)
    	)]
    #pragma warning disable S101 // Types should be named in PascalCase
    	public class ABCHttpApiHostModule : AbpModule
    #pragma warning restore S101 // Types should be named in PascalCase
    	{
    		public override void ConfigureServices(ServiceConfigurationContext context)
    		{
    			if (context == null)
    			{
    				throw new ArgumentNullException(nameof(context));
    			}
    
    			var configuration = context.Services.GetConfiguration();
    
    			ConfigureUrls(configuration);
    			ConfigureConventionalControllers();
    			ConfigureAuthentication(context, configuration);
    			ConfigureSwagger(context, configuration);
    			ConfigureVirtualFileSystem(context);
    			ConfigureCors(context, configuration);
    			ConfigureExternalProviders(context);
    			ConfigureHealthChecks(context);
    
    			// Background jobs are executed in background process manager application
    			Configure<AbpBackgroundJobOptions>(options => options.IsJobExecutionEnabled = false);
    
    			ApplicationInsightsConfigurationHelper.AddApplicationInsightsForHttpApplications(context, configuration, Assembly.GetExecutingAssembly().GetName().Name);
    		}
    
    #pragma warning disable CA1822 // Method can be made static
    		private void ConfigureHealthChecks(ServiceConfigurationContext context)
    #pragma warning restore CA1822 // Method can be made static
    		{
    			context.Services.AddABCHealthChecks();
    		}
    
    		private void ConfigureUrls(IConfiguration configuration)
    		{
    			Configure<AppUrlOptions>(options =>
    			{
    				options.Applications["Angular"].RootUrl = configuration["App:AngularUrl"];
    				options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] =
    					"account/reset-password";
    				options.Applications["Angular"].Urls[AccountUrlNames.EmailConfirmation] =
    					"account/email-confirmation";
    			});
    		}
    
    		private void ConfigureVirtualFileSystem(ServiceConfigurationContext context)
    		{
    			var hostingEnvironment = context.Services.GetHostingEnvironment();
    
    			if (hostingEnvironment.IsDevelopment())
    			{
    				Configure<AbpVirtualFileSystemOptions>(options =>
    				{
    					options.FileSets.ReplaceEmbeddedByPhysical<ABCObjectControlDomainSharedModule>(
    						Path.Combine(hostingEnvironment.ContentRootPath,
    							String.Format("..{0}..{0}src{0}ABC.ObjectControl.Domain.Shared", Path.DirectorySeparatorChar)));
    					options.FileSets.ReplaceEmbeddedByPhysical<ABCObjectControlDomainModule>(
    						Path.Combine(hostingEnvironment.ContentRootPath,
    							String.Format("..{0}..{0}src{0}ABC.ObjectControl.Domain", Path.DirectorySeparatorChar)));
    					options.FileSets.ReplaceEmbeddedByPhysical<ABCObjectControlApplicationContractsModule>(
    						Path.Combine(hostingEnvironment.ContentRootPath,
    							String.Format("..{0}..{0}src{0}ABC.ObjectControl.Application.Contracts",
    								Path.DirectorySeparatorChar)));
    					options.FileSets.ReplaceEmbeddedByPhysical<ABCObjectControlApplicationModule>(
    						Path.Combine(hostingEnvironment.ContentRootPath,
    							String.Format("..{0}..{0}src{0}ABC.ObjectControl.Application", Path.DirectorySeparatorChar)));
    					options.FileSets.ReplaceEmbeddedByPhysical<ABCObjectControlHttpApiModule>(
    						Path.Combine(hostingEnvironment.ContentRootPath,
    							String.Format("..{0}..{0}src{0}ABC.ObjectControl.HttpApi", Path.DirectorySeparatorChar)));
    
    					options.FileSets.ReplaceEmbeddedByPhysical<MasterDataManagementDomainSharedModule>(
    						Path.Combine(hostingEnvironment.ContentRootPath,
    							String.Format("..{0}..{0}modules{0}ABC.MasterDataManagement{0}src{0}ABC.MasterDataManagement.Domain.Shared",
    								Path.DirectorySeparatorChar)));
    
    					options.FileSets.ReplaceEmbeddedByPhysical<AnalyticsDomainSharedModule>(
    						Path.Combine(hostingEnvironment.ContentRootPath,
    							String.Format("..{0}..{0}modules{0}ABC.Analytics{0}src{0}ABC.Analytics.Domain.Shared",
    								Path.DirectorySeparatorChar)));
    
    					options.FileSets.ReplaceEmbeddedByPhysical<ItemPortfolioManagementDomainSharedModule>(
    						Path.Combine(hostingEnvironment.ContentRootPath,
    							String.Format("..{0}..{0}modules{0}ABC.ItemPortfolioManagement{0}src{0}ABC.ItemPortfolioManagement.Domain.Shared",
    								Path.DirectorySeparatorChar)));
    				});
    			}
    		}
    
    		private void ConfigureConventionalControllers()
    		{
    			Configure<AbpAspNetCoreMvcOptions>(options =>
    				options.ConventionalControllers.Create(typeof(ABCObjectControlApplicationModule).Assembly)
    			);
    		}
    
    		private static void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
    		{
    			var authenticationBuilder = context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    				.AddJwtBearer(options =>
    				{
    					options.Authority = configuration["AuthServer:Authority"];
    					options.RequireHttpsMetadata =
    						Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
    					options.Audience = "ABC";
    				});
    
    			if (configuration.GetValue<bool?>("AzureAd:IsEnbaled") is true)
    			{
    				authenticationBuilder.AddOpenIdConnect("AzureOpenId", "Azure AD", options =>
    				{
    					options.Authority = "https://login.microsoftonline.com/" + configuration["AzureAd:TenantId"] + "/v2.0/";
    					options.ClientId = configuration["AzureAd:ClientId"];
    					options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
    					options.CallbackPath = configuration["AzureAd:CallbackPath"];
    					options.ClientSecret = configuration["AzureAd:ClientSecret"];
    					options.RequireHttpsMetadata = false;
    					options.SaveTokens = true;
    					options.GetClaimsFromUserInfoEndpoint = true;
    					options.Scope.Add("email");
    
    					options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
    				});
    			}
    		}
    
    		private static void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration)
    		{
    			context.Services.AddAbpSwaggerGenWithOAuth(
    				configuration["AuthServer:Authority"],
    				new Dictionary<string, string> { { "ABC", "ABC API" } },
    				options =>
    				{
    					options.SwaggerDoc("v1", new OpenApiInfo { Title = "ABC API", Version = "v1" });
    					options.DocInclusionPredicate((docName, description) => true);
    					options.CustomSchemaIds(type => type.FullName);
    
    					var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    					if (basePath != null)
    					{
    						foreach (var filePath in Directory.GetFiles(Path.Combine(basePath), "*.xml"))
    						{
    							options.IncludeXmlComments(filePath);
    						}
    					}
    				});
    		}
    
    		private static void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
    		{
    			context.Services.AddCors(options =>
    			{
    				options.AddDefaultPolicy(builder =>
    				{
    					builder
    						.WithOrigins(
    							configuration["App:CorsOrigins"]
    								.Split(",", StringSplitOptions.RemoveEmptyEntries)
    								.Select(o => o.Trim().RemovePostFix("/"))
    								.ToArray()
    						)
    						.WithAbpExposedHeaders()
    						.SetIsOriginAllowedToAllowWildcardSubdomains()
    						.AllowAnyHeader()
    						.AllowAnyMethod()
    						.AllowCredentials();
    				});
    			});
    		}
    
    		private static void ConfigureExternalProviders(ServiceConfigurationContext context)
    		{
    			context.Services
    				.AddDynamicExternalLoginProviderOptions<GoogleOptions>(
    					GoogleDefaults.AuthenticationScheme,
    					options =>
    					{
    						options.WithProperty(x => x.ClientId);
    						options.WithProperty(x => x.ClientSecret, isSecret: true);
    					}
    				)
    				.AddDynamicExternalLoginProviderOptions<MicrosoftAccountOptions>(
    					MicrosoftAccountDefaults.AuthenticationScheme,
    					options =>
    					{
    						options.WithProperty(x => x.ClientId);
    						options.WithProperty(x => x.ClientSecret, isSecret: true);
    					}
    				)
    				.AddDynamicExternalLoginProviderOptions<TwitterOptions>(
    					TwitterDefaults.AuthenticationScheme,
    					options =>
    					{
    						options.WithProperty(x => x.ConsumerKey);
    						options.WithProperty(x => x.ConsumerSecret, isSecret: true);
    					}
    				);
    		}
    
    		public override void OnApplicationInitialization(ApplicationInitializationContext context)
    		{
    			var app = context.GetApplicationBuilder();
    			var env = context.GetEnvironment();
    
    			if (env.IsDevelopment())
    			{
    				app.UseDeveloperExceptionPage();
    			}
    
    			app.UseAbpRequestLocalization();
    			app.UseDefaultFiles();
    			app.UseStaticFiles();
    			app.UseRouting();
    			app.UseCors();
    			app.UseAuthentication();
    
    			if (MultiTenancyConsts.IS_ENABLED)
    			{
    				app.UseMultiTenancy();
    			}
    
    			app.UseAuthorization();
    			app.UseSwagger();
    			app.UseAbpSwaggerUI(options =>
    			{
    				options.SwaggerEndpoint("/swagger/v1/swagger.json", "ABC API");
    
    				var configuration = context.GetConfiguration();
    				options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
    			});
    			app.UseAuditing();
    			app.UseAbpSerilogEnrichers();
    			app.UseUnitOfWork();
    			app.UseConfiguredEndpoints();
    		}
    	}
    }
    
    
  • User Avatar
    0
    JanneHarju created

    Those logs what I give has all related logs what happened when I start to login give correct account informations and return back to application. Because there was so much logs I filtered these kind of lines away because I assume they are not related to this issue: customDimensions.SourceContext !contains "HealthChecks" and message != "Added 0 entity changes to the current audit log" and message !contains "Added bundle 'Lepton.Global'"

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    The Module code is no problem, and as you said it works locally.

    Could you add the following code to your module class(AuthServer and HttpApi.Host) :

    Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;

    Change your logging level to debug, then redeploy and share the logs.

    BTW, could you share the Txt file instead of CVS? thanks.

  • User Avatar
    0
    JanneHarju created

    Here are logs when I did one login. There was on ly csv export possibility in our logging tool, but I copied data from there to txt format if you prefer it. https://drive.google.com/drive/folders/1yTPoOMqUpcpTjlXgdEKsSJahgIRTd65A?usp=drive_link

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Bearer was not authenticated. Failure message: IDX10205: Issuer validation failed. Issuer: 'https://auth.scm-test.lw.app/'. Did not match: validationParameters.ValidIssuer: 'null' or validationParameters.ValidIssuers: 'https://app-scm-auth-test-qa-001.ase-sharedeawgeacbyumuk-qa-001.appserviceenvironment.net/' or validationParameters.ConfigurationManager.CurrentConfiguration.Issuer: 'Null'

    I think this may be related to your environment configuration

    You can check this: https://support.abp.io/QA/Questions/3312/I-am-getting-redirected-back-to-the-login-page-after-a-successful-login#answer-6494c158-4516-8e1d-b79d-3a04a75ad57c

  • User Avatar
    0
    JanneHarju created

    We have Application Gateway which direct calls to App service. That appserviceenvironment.net is app service address. And lw.app is Application gateway address. Can you tell me is that Issuer: 'https://auth.scm-test.lw.app/' value from fronend Authority parameter? And can you tell me where this value is coming from validationParameters.ValidIssuers: 'https://app-scm-auth-test-qa-001.ase-sharedeawgeacbyumuk-qa-001.appserviceenvironment.net/'? I mean from what configuration. Appsettings.json?, database? Because I'm sure that there is no where that exact value. But in earlier project when some url changed from lw.app to appserviceenvironment.net there was missing trailing / . And adding / fixed. So now I'm wondering is it same problem and if it is where I'm missing that / . Because I think I have added it to all places.

  • User Avatar
    0
    JanneHarju created

    here is example token payload. If it tells you enything.

    {
      "sub": "f6277149-bcd8-7587-5001-3a096665a3a1",
      "preferred_username": "admin",
      "email": "SCMadmin@leanware.fi",
      "role": "admin",
      "given_name": "admin",
      "phone_number_verified": "False",
      "email_verified": "False",
      "unique_name": "admin",
      "oi_prst": "SCM_App",
      "iss": "https://auth.scm-test.lw.app/",
      "oi_au_id": "0a696354-cb8f-b108-c129-3a666cdeae51",
      "client_id": "SCM_App",
      "oi_tkn_id": "5ba7a0c9-4577-a3ea-a15a-3a666ab74e8c",
      "aud": "SCM",
      "scope": "offline_access openid profile roles email phone SCM",
      "jti": "fe00a5cf-c813-4713-9219-85e04666cc29",
      "exp": 1702564553,
      "iat": 1702560953
    }
    
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    It happens because the issuer is automatically set by the request. you can try:

    PreConfigure<OpenIddictServerBuilder>(builder =>
    {
        builder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));
        builder.AddEncryptionCertificate(GetSigningCertificate(hostingEnvironment, configuration));
        builder.SetIssuer(new Uri(configuration["AuthServer:Authority"]!));
        builder.Configure(c =>
        {
            c.TokenValidationParameters.ValidIssuers = new List<string>()
            {
                configuration["AuthServer:Authority"]!,
                "https://app-scm-auth-test-qa-001.ase-sharedeawgeacbyumuk-qa-001.appserviceenvironment.net/"
            };
        });
    });
    
  • User Avatar
    0
    JanneHarju created

    Thank you for the tip. Actually in my stash I did have that setIssuer because there was that line in example project what I created with abp suite, but I wasn't sure was that needed because there wasn't anything in migration guide and there was not need for it with IdentityServer. I will first only add it and if it doesn't work then I will add that configure part.

  • User Avatar
    0
    JanneHarju created

    Actually I forget that there was override for AuthServer:Authority parameter at Host App Service Configuration and it was https://app-scm-auth-test-qa-001.ase-sharedeawgeacbyumuk-qa-001.appserviceenvironment.net/ because that way communication goes directly to auth server inside Azure that why we cannot use public address(https://auth.scm-test.lw.app) because it is IP restricted. After I added that setIssuer to AuthServer code it started to work. At AuthServer AuthServer:Authority parameter value is https://auth.scm-test.lw.app/. So now it started to work. So there was no need for that Configure part. SetIssuer was all I needed.

    builder.Configure(c =>
        {
            c.TokenValidationParameters.ValidIssuers = new List<string>()
            {
                configuration["AuthServer:Authority"]!,
                "https://app-scm-auth-test-qa-001.ase-sharedeawgeacbyumuk-qa-001.appserviceenvironment.net/"
            };
        });
    

    Thanks.

  • User Avatar
    0
    JanneHarju created

    Is it normal/correct that after login there is iss url parameter like this: https://scm-test.lw.app/app/?iss=https:%2F%2Fauth.scm-test.lw.app%2F ?

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Yes, it's normal.

    You can see: https://github.com/openiddict/openiddict-core/issues/1394

  • User Avatar
    0
    JanneHarju created

    Is it OK to clean it away in our own code? https://scm-test.lw.app/? code=0m3Bw75YTPhX5MPp2lmcGhiU4igYl1YMRWDbD0QIEMA& state=dmxqdENEanJ1ZWFseU5sTGZjRXZpZklDYnQ5Ni5Cemg3dFBWcC1vN3Bja2V5& iss=https%3A%2F%2Fauth.scm-test.lw.app%2F

    These are what AuthServer returns in callback call. Code and state are removed from url by your code or open iddicts but iss stays.

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    I'm not an openid expert You can get help from openiddict: https://github.com/openiddict/openiddict-core/issues

  • User Avatar
    0
    JanneHarju created

    Ok. Thanks anyway.

Made with ❤️ on ABP v9.1.0-preview. Updated on December 02, 2024, 12:35