Quantcast
Channel: ASP.NET Core
Viewing all articles
Browse latest Browse all 9386

Kept running into 401 unauthorize response using .NET Core v3.1

$
0
0

<div>I upgraded the source code to .NET Core v3.1 & I'm having trouble figuring how to debug the backend issue due to lot of dependency injections, abstractions & overriding classes/methods all over.  The employee who wrote this have overcomplicate things & he had left the company so we got stuck with the confusing source code mess here that take a lot of our time & energy, to make sense of the it.  :-/</div> <div> </div> <div>

The error I'm having is a 401 unauthorize response.  I discovered the debugger doesnt respond in StartUp class when consuming the webservice, it only respond when you start up the Web App.  So, it took us a while & finally found a hitting debugger breakpoint on a MVC controller page to point us in the right direction.  There it is saying the Identity is not authenticated so that explain the unauthorize error.</div> <div> </div> <div>

We're not familiar with this one authentication technology, `Odachi`.  We believe there are 2 seperate authentication architecture, which is ehe WebApp's webpages login authorization for the customer's web browser & Odachi deal with the WebApp's webservice login authorization for the 3rd party software making the webservice call.</div> <div> </div> <div>

Source code below is the webservice MVC controller w/ Authorization filter.   Then further down will be the Startup w/ base Startup abstraction.</div> <div></div> <div>

[ Webservice call ]</div> <div></div> <div>

namespace ABC.Payments.AspNet.MVC
{
    public class AuthorizeWithNoChallengeFilterAttribute : IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            if (context.HttpContext.User?.Identity.IsAuthenticated != true)
                context.Result = new UnauthorizedResult();
            }
        }
    }

</div> <div></div> <div>

namespace ABC.Payments.MerchantWeb.Api
{
    [TypeFilter(typeof(AuthorizeWithNoChallengeFilterAttribute))]
    public class MerchantsV1Controller : Controller
    {
        [Route("api/v1/merchants/{merchantAccountId}/customers/payments/paymentmethods"), HttpPost]
        public async Task<ObjectResult> Payment([FromBody] ItemInfoViewModel itemInfo, CancellationToken cancellationToken)
        {
            var payments = whatever();
            return new HttpNotAcceptableObjectResult(payments);
        }
    }
}

</div> <div></div> <div>

[ Startup & Base Startup ]

</div> <div></div> <div> <div>

    namespace ABC.Payments.MerchantWeb</div> <div>   

{</div> <div>

        // StackOverflow Post on how to find 401 Unathorize error (debug)</div> <div> 

      // --> https://stackoverflow.com/questions/43574552/authorization-in-asp-net-core-always-401-unauthorized-for-authorize-attribute</div> <div> </div> <div>        public class Startup : StartupBase<MerchantRequestContext, Merchant></div> <div>

        {</div> <div>

            private const string _schemeCustomMerchantBasic = "CustomMerchantBasic";</div> <div> </div> <div>

            public Startup(IWebHostEnvironment webHostEnvironment)</div> <div>                : base(webHostEnvironment, PortalRoleType.Merchant)</div> <div>

            {</div> <div>

            }</div> <div> </div> <div>

            public void ConfigureServices(IServiceCollection services)</div> <div>

            {</div> <div>

                base._configureServices(true, services);</div> <div> </div> <div> 

              services.AddTransient(sp => sp.GetService<MerchantRequestContext>()?.Merchant);</div> <div>

                services.AddTransient(sp => sp.GetService<MerchantRequestContext>()?.Customer);</div> <div>

                services.AddTransient(sp => sp.GetService<MerchantRequestContext>()?.TenantSettings);</div> <div> </div> <div>

                services.AddLocalization(options =></div> <div>

                    {</div> <div> 

                      options.ResourcesPath = "Resources";</div> <div>

                    });</div> <div> 

              services.AddMvcCore()</div> <div>

                    .AddViewLocalization(LanguageViewLocationExpanderFormat.SubFolder, setup =></div> <div> 

                  {</div> <div>

                        setup.ResourcesPath = "Resources";</div> <div> 

                  })</div> <div> 

                  .AddDataAnnotationsLocalization()</div> <div> 

                  .AddApiExplorer();</div> <div> </div> <div>

                services.AddCors(options =></div> <div>

                    {</div> <div>

                        options.AddPolicy("Internal", p => p.WithOrigins(base._configuration["Cors:InternalSource"]).WithMethods("POST").WithHeaders("accept", "request", "authorization", "content-type", "internal"));</div> <div>

                    });</div> <div> </div> <div>

                services.AddAuthentication()</div> <div>

    // https://github.com/Kukkimonsuta/Odachi/blob/master/src/Odachi.AspNetCore.Authentication.Basic/Events/BasicSignInContext.cs  (Basic Sign Context)</div> <div>

    // https://github.com/Kukkimonsuta/Odachi/blob/master/samples/BasicAuthenticationSample/Startup.cs</div> <div>

                    .AddBasic(_schemeCustomMerchantBasic, options =></div> <div>

                    {</div> <div>

    //    ////////Notice: AutomaticChallenge is depreciated, google search said to use DefaultChallengeScheme w/ given cookie-authentication-scheme but that still doesnt explain how to disable it</div> <div>

    //    ////////        https://stackoverflow.com/questions/45878166/asp-net-core-2-0-disable-automatic-challenge</div> <div>

    //    ////////        https://github.com/dotnet/aspnetcore/issues/2007</div> <div> 

                      //## options.AutomaticChallenge = false;</div> <div>

                        options.Realm = "AutoPayment API v1";</div> <div>

                        options.Events = new BasicEvents()</div> <div>

                        {</div> <div>

                            OnSignIn = async context =></div> <div>

                            {</div> <div> 

                              var claims = new List<Claim>();</div> <div> </div> <div> 

                              if (context.Username == "ndi3DanDba993nvbaqbn3d93" && context.Password == "aVd3Ed51dfDE5acCCni9l1IxPq9")</div> <div> 

                                  claims.Add(new Claim(ClaimTypes.Role, "InternalAPIUser"));</div> <div> 

                              else</div> <div>

                                {</div> <div>

                                    string merchantAccountId = context.Request.Path.Value.Split('/').Skip(4).FirstOrDefault();</div> <div>

                                    var merchantRepository = context.HttpContext.RequestServices.GetRequiredService<IMerchantRepository>();</div> <div> </div> <div>   

                                if (merchantAccountId == null || merchantAccountId.Length != 14 || merchantAccountId.Split('-').Length != 3)</div> <div>

                                        throw new Exception($"Invalid merchant account Id ({merchantAccountId ?? string.Empty}).");</div> <div> </div> <div>

                                    var merchant = await merchantRepository.GetMerchantAsync(merchantAccountId, context.HttpContext.RequestAborted);</div> <div> 

                                  if (merchant == null || !merchant.IsActive || (merchant.GatePayApiKey != context.Username || merchant.GatePayApiSecret != context.Password))</div> <div> 

                                  {</div> <div>

                                        context.Fail("Invalid merchant"); //## context.HandleResponse();</div> <div> 

                                      return;</div> <div> 

                                  }</div> <div>

                                }</div> <div> </div> <div>

                                var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));  //## options.AuthenticationScheme));</div> <div> 

                              context.Principal = principal;</div> <div> </div> <div>

                                //## context.Ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), options.AuthenticationScheme);</div> <div>

                                context.Success(); //## context.HandleResponse();</div> <div>

                                //return Task.CompletedTask;</div> <div>

                            }</div> <div> 

                      };</div> <div>

                    });</div> <div> 

          }</div> <div> </div> <div>

            public void Configure(IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory, IServiceProvider serviceProvider)</div> <div> 

          {</div> <div>

                base._configure(true, applicationBuilder, loggerFactory, serviceProvider);</div> <div> </div> <div> 

              applicationBuilder.UseCors("Internal");</div> <div> </div> <div> 

              applicationBuilder.UseWhen(context => !context.Request.Path.StartsWithSegments(new PathString("/api/v1")), b => b.UseAuthentication());</div> <div> 

          }</div> <div> 

      }</div> <div>

    }</div>

<div>    namespace ABC.Payments</div> <div>

    {</div> <div>

        public class StartupBase<TRequestContext, TUserContext> </div> <div>

            where TRequestContext : RequestContext<TUserContext></div> <div>

        {</div> <div>

            public StartupBase(IWebHostEnvironment webHostEnvironment, PortalRoleType portalRoleType)</div> <div>

            {</div> <div> 

              _portalRoleType = portalRoleType;</div> <div> 

              _webHostEnvironment = webHostEnvironment;</div> <div> </div> <div>

                var builder = new ConfigurationBuilder();</div> <div> 

              ConfigurationLoader.Load(builder, webHostEnvironment);</div> <div> 

              _configuration = builder.Build();</div> <div> </div> <div>

                if (webHostEnvironment.EnvironmentName.Equals("Production", StringComparison.OrdinalIgnoreCase) == true && _configuration["ConfirmProduction"]?.Equals("Yes", StringComparison.OrdinalIgnoreCase) != true)</div> <div>

                    throw new Exception("Azure defaults to \"Production\" for the environment, so you need to create an AppSetting of \"ConfirmProduction\" to \"Yes\" to ensure that is the intent.");</div> <div> 

          }</div> <div> </div> <div>

            private readonly IWebHostEnvironment _webHostEnvironment;</div> <div> 

          public readonly IConfiguration _configuration;</div> <div>

            private readonly PortalRoleType _portalRoleType;</div> <div> </div> <div>

            public void _configureServices(bool isWebBrowserFrontendGui, IServiceCollection services)</div> <div>

            {</div> <div>

                if (isWebBrowserFrontendGui)</div> <div> 

              {</div> <div>

                    services.AddDistributedRedisCache(options =></div> <div> 

                      {</div> <div>

                            options.Configuration = _configuration["Storage:Redis:Configuration"];</div> <div> 

                      });</div> <div> 

                  services.AddSingleton<RedisCache>();</div> <div> 

                  services.AddSingleton<MemoryDistributedCache>();</div> <div> 

                  services.AddSingleton<IDistributedCache>(</div> <div> 

                          sp => new ResilientDistributedCache(sp.GetRequiredService<RedisCache>(), sp.GetRequiredService<MemoryDistributedCache>())</div> <div> 

                      );</div> <div> </div> <div>

                    var azureBlobConnectionTring = _configuration["Storage:AzureBlob:ConnectionString"];</div> <div>

                    if (azureBlobConnectionTring != null)</div> <div>

                    {</div> <div>

                        var storageAccount = CloudStorageAccount.Parse(azureBlobConnectionTring);</div> <div>

                        var client = storageAccount.CreateCloudBlobClient();</div> <div> 

                      var azureBlobContainer = client.GetContainerReference("dataprotection-key-container");</div> <div> 

                      services.AddDataProtection().PersistKeysToAzureBlobStorage(azureBlobContainer, "keys.xml");</div> <div> 

                  }</div> <div> </div> <div> 

                  services.AddSession(options =></div> <div>

                    {</div> <div>

                        //options.IdleTimeout = TimeSpan.FromMinutes(5);</div> <div>

                    });</div> <div> </div> <div> 

                  services.AddDefaultIdentity<ApplicationUser>()</div> <div> 

                      .AddRoles<IdentityRole<Guid>>()</div> <div>

                        .AddEntityFrameworkStores<ApplicationContext>()  // FYI - AddEntityFrameworkStores() deal with role that derives from IdentityRole, as per documentation.</div> <div> 

                      .AddDefaultTokenProviders();</div> <div>

                    services.ConfigureApplicationCookie(options => {</div> <div> 

                      options.LoginPath = new PathString("/Home/Index");</div> <div>

                        options.SlidingExpiration = true;</div> <div>

                        options.ExpireTimeSpan = TimeSpan.FromMinutes(_configuration.GetValue<int?>("Authentication:SlidingExpirationTime").Value);</div> <div>

                        options.AccessDeniedPath = new PathString("/Home/AccessDenied");</div> <div> 

                  });</div> <div>

                    services.Configure<IdentityOptions>(options => {</div> <div> 

                      options.Password.RequireUppercase = false;</div> <div> 

                      options.Password.RequireLowercase = false;</div> <div>

                        options.Password.RequireNonAlphanumeric = false;</div> <div> 

                      options.Password.RequireDigit = false;</div> <div> 

                      options.Password.RequiredLength = 7;</div> <div>

                    });</div> <div> </div> <div> 

                  services.AddControllersWithViews();</div> <div> 

                  services.AddRazorPages();</div> <div> </div> <div> 

                  // AddMvc() vs AddMvcCore() explaination found at --> https://offering.solutions/blog/articles/2017/02/07/the-difference-between-addmvc-and-addmvccore/</div> <div>

                    //                                                --> https://stackoverflow.com/questions/42365275/how-to-implement-a-pure-asp-net-core-web-api-by-using-addmvccore/42365276#42365276</div> <div>

                   services.AddMvc().AddRazorRuntimeCompilation();</div> <div> </div> <div> 

                 services.Configure<MvcRazorRuntimeCompilationOptions>();</div> <div> 

                 services.Configure<AuthorizationOptions>(options =></div> <div> 

                 {</div> <div> 

                      options.DefaultPolicy = AuthorizationPolicy.Combine(options.DefaultPolicy,</div> <div> 

                          new AuthorizationPolicy(new IAuthorizationRequirement[] {</div> <div> 

                          new RolesAuthorizationRequirement(new string[] { _portalRoleType.ToString(), PortalRoleType.Internal.ToString() }),</div> <div>

                            new ImpersonationNotExpiredAuthorizationRequirement(_portalRoleType, _configuration.GetValue<TimeSpan?>("Authentication:ImpersonationTimeLimit").Value)</div> <div>

                            }, new string[0]));</div> <div> 

                  });</div> <div> </div> <div>   

                services.AddMvcCore(options =></div> <div>

                    {</div> <div>

                        var requestContextAttribute = new LoadRequestContextAttribute(typeof(TRequestContext));</div> <div> 

                      options.Filters.Add(requestContextAttribute); </div> <div> </div> <div> </div> <div> 

  options.ModelBinderProviders[options.ModelBinderProviders.IndexOf(</div> <div> </div> <div>

      options.ModelBinderProviders.OfType<ComplexTypeModelBinderProvider>().First()</div> <div> </div> <div> 

  )] = new TryServicesModelBinderProvider(services.BuildServiceProvider());</div> <div> 

                      options.ModelBinderProviders.Insert(0, new EnumModelBinderProvider(services.BuildServiceProvider()));</div> <div> 

                  })</div> <div> </div> <div> 

                  .AddDataAnnotationsLocalization()</div> <div> 

                  .AddNewtonsoftJson(settings =></div> <div> 

                  {</div> <div> 

                      settings.SerializerSettings.ContractResolver = new DefaultContractResolver();</div> <div> 

                  });</div> <div> </div> <div> 

                  services.Configure<ForwardedHeadersOptions>(options => options.RequireHeaderSymmetry = false);</div> <div>

                }</div> <div> </div> <div>

                //services.AddPayments<TRequestContext, TUserContext>(_configuration, string.Empty);</div> <div>

            }</div> <div> </div> <div> 

              public void _configure(bool isWebBrowserFrontendGui, IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory, IServiceProvider serviceProvider)</div> <div> 

          {</div> <div>

               if (isWebBrowserFrontendGui)</div> <div> 

              {</div> <div> 

                  serviceProvider.GetRequiredService<ITelemeter<StartupBase>>().TrackMetric("Startup Time", (DateTime.UtcNow - DateTime.UtcNow).TotalSeconds);</div> <div> </div> <div> 

                  // Exception Page Handling.</div> <div>

                    if (!_webHostEnvironment.IsProduction())</div> <div> 

                  {</div> <div> 

                      applicationBuilder.UseDeveloperExceptionPage();</div> <div> 

                      //applicationBuilder.UseDatabaseErrorPage();</div> <div> 

                  }</div> <div> 

                  else</div> <div>

                        applicationBuilder.UseExceptionHandler("/Home/ErrorPage.html");</div> <div> </div> <div> 

                  applicationBuilder.UseStaticFiles(); // Note, we are not authenticating for static files if this is before them</div> <div>   

                //applicationBuilder.UseStatusCodePages();</div> <div> </div> <div> 

                  // Session.</div> <div> 

                  applicationBuilder.UseSession();</div> <div> </div> <div> 

                  applicationBuilder.UseAuthentication();</div> <div> </div> <div> 

                  // Routing.</div> <div> 

                  applicationBuilder.UseRouting();</div> <div> 

                  applicationBuilder.UseAuthorization();  // Exception error said to put this between UseRouting() & UseEnpoint().</div> <div> 

                  applicationBuilder.UseEndpoints(endpoints =></div> <div>

                    {</div> <div> 

                      endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");</div> <div>

                        endpoints.MapRazorPages();</div> <div> 

                  });</div> <div> </div> <div>

                    // Config Localization.</div> <div> 

                  var options = serviceProvider.GetService<IOptions<RequestLocalizationOptions>>();</div> <div> 

                  if (options != null)</div> <div> 

                      applicationBuilder.UseRequestLocalization(options.Value);</div> <div> </div> <div> 

                  applicationBuilder.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.All });</div> <div> </div> <div> 

                  // Ensure Https.</div> <div> 

                  var portals = applicationBuilder.ApplicationServices.GetRequiredService<Portals>();</div> <div> 

                  applicationBuilder.Use(next => async httpContext =></div> <div> 

                  {</div> <div> 

                      if (httpContext.Request.Host.Value.Contains("localhost"))</div> <div>

                        {</div> <div> 

                          await next(httpContext);</div> <div> 

                      }</div> <div> 

                      else</div> <div> 

                      {</div> <div> 

                          string host = portals.GetHostForRedirect(httpContext.Request.Host.Value);</div> <div> 

                          if (!host.Equals((httpContext.Request.IsHttps ? "https://" : "http://") + httpContext.Request.Host, StringComparison.OrdinalIgnoreCase))</div> <div>         

                      httpContext.Response.Redirect($"{host}{httpContext.Request.Path}{httpContext.Request.QueryString}");</div> <div>

                            else</div> <div> 

                              await next(httpContext);</div> <div> 

                      }</div> <div> 

                  });</div> <div>   

            }</div> <div> </div> <div> 

              //applicationBuilder.UsePayments<TRequestContext, TUserContext>();</div> <div>

           }</div> <div> 

      }</div> <div> 

  }</div> <div></div> </div>


Viewing all articles
Browse latest Browse all 9386

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>