IrfaanTJJ
IrfaanTJJ

Reputation: 21

CORS Error: LinkedIn Authentication; .NET Core 5 REST Api

Technology Stack

  1. Clicking on a Register Button, calls the Register Component.
  2. Component within a useEffect(), calls "/login URL using Axios
  3. C# Map("/login") is called a Challenge to Authenticate using LinkedIn
  4. The CORS Error is then returned

Error Snapshot 1 of 5 Snapshot 2 of 5; Snapshot 3 of 5; Snapshot 4 of 5; Snapshot 5 of 5

React Code

      const url = `/login`;

      const headers = {
        'Content-Type': 'text/html'
      }

      axios({
        method: 'get',
        url: url,
        headers: headers
      })
        .then((response) => {...})
        .catch((error: Error | AxiosError) => {...});

C# Code - Linked Authentication, Cookies, CORS Middleware

Start.cs - ConfigureServices()

        public void ConfigureServices(IServiceCollection services)
        {
            #region AddAuthentication, AddLinkedIn, AddCookie
            services.AddAuthentication()
                .AddLinkedIn(o =>
                {
                    IConfigurationSection linkedinAuthNSection = 
                        Configuration.GetSection("Authentication:Linkedin");

                    o.ClientId = linkedinAuthNSection["ClientId"];
                    o.ClientSecret = linkedinAuthNSection["ClientSecret"];
                })
                .AddCookie(o =>
                {
                    o.LoginPath = "/login";
                    o.LogoutPath = "/logout";
                });
            #endregion

            #region Global CORS Policy Declaration
            services.AddCors(o =>
            {
                o.AddDefaultPolicy(builder =>
                    builder.AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowAnyOrigin()
                );
            });
            #endregion 

            services.AddControllersWithViews();

            // In production, the React files will be served from this directory
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "client-app/build";
            });
        }

Start.cs - Configure()

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            #region Map/login
            app.Map("/login", builder =>
            {
                builder.Run(async context =>
                {
                    var properties = new AuthenticationProperties() { RedirectUri = "/" };

                    await context.ChallengeAsync("LinkedIn", properties);
                });
            });
            #endregion

            #region Map/logout
            app.Map("/logout", builder =>
            {
                builder.Run(async context =>
                {
                    await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

                    context.Response.Redirect("/");
                });
            });
            #endregion

            app.UseStaticFiles();
            app.UseSpaStaticFiles();
            app.UseRouting();

            app.UseCors();

            app.UseAuthentication();
            app.UseAuthorization();

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

            app.UseSpa(spa =>
            {
                spa.Options.SourcePath = Path.Join(env.ContentRootPath, "client-app");

                if (env.IsDevelopment())
                {
                    spa.Options.StartupTimeout = TimeSpan.FromSeconds(240);
                    spa.UseReactDevelopmentServer(npmScript: "start");
                }
            });
        }

Upvotes: 0

Views: 482

Answers (2)

IrfaanTJJ
IrfaanTJJ

Reputation: 21

Finally, I got it working!

I have made 2 major changes, after trying to fathom, understand internalizing. Thanks to the yellow warning symbols ⚠ of Chrome Dev Tools, that let me to this article and change 1 of the solution.

Change 1

  • Applied the major snippets of the above example code to my React SPA .NET Core project

  • Dropped the Map Middleware (app.Map("/login") that allows branching of the pipeline path.

  • In favor for a .NET Controller/Action.

  • However more specifically, just Action since the "/login" is added to the path, of the URL, which makes it difficult to accept the successful sign-in.

Change 2

Authentication.cs

    //[Route("[controller]/[action]")]
    [Route("[action]")]
    public class AuthenticationController : Controller
    {
        [HttpGet]
        public IActionResult Register(string authType = "LinkedIn")
        {
            return Challenge(new AuthenticationProperties() { RedirectUri = "/" });
        }

        [HttpGet]
        public IActionResult Login(string authType = "LinkedIn")
        {
            return Challenge(new AuthenticationProperties() { RedirectUri = "/" });
        }

        [HttpGet]
        public IActionResult Logout()
        {
            return SignOut();
        }

Start.cs ConfigureServices()

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();

            services.AddAuthentication(o =>
            {
                o.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                o.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                o.DefaultChallengeScheme = "LinkedIn";
            })
               .AddCookie()
               .AddOAuth("LinkedIn", o =>
               {
                   o.CorrelationCookie.HttpOnly = true;
                   o.CorrelationCookie.SameSite = SameSiteMode.Lax;
                   
                   var linkedInSection = Configuration.GetSection("Authentication:LinkedIn");

                   o.ClientId = linkedInSection.GetSection("ClientId").Get<string>();
                   o.ClientSecret = linkedInSection.GetSection("ClientSecret").Get<string>();
                   o.CallbackPath = new PathString(linkedInSection.GetSection("CallbackPath").Get<string>());

                   o.AuthorizationEndpoint = linkedInSection.GetSection("AuthorizationEndpoint").Get<string>();
                   o.TokenEndpoint = linkedInSection.GetSection("TokenEndpoint").Get<string>();
                   o.UserInformationEndpoint = linkedInSection.GetSection("UserInformationEndpoint").Get<string>();

                   o.Scope.Add("r_liteprofile");
                   o.Scope.Add("r_liteprofile");

                   o.Events = new OAuthEvents
                   {
                       OnCreatingTicket = async context =>
                       {
                           var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
                           request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                           request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);

                           var response = await context.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
                           response.EnsureSuccessStatusCode();

                           var json = JsonDocument.Parse(await response.Content.ReadAsStringAsync());

                           context.RunClaimActions(json.RootElement);
                       }
                   };
               });
        }

Start.cs Configure()

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapRazorPages();
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller}/{action=Index}/{id?}");
            });
        }

Upvotes: 1

igmani
igmani

Reputation: 236

In your Chrome browser, try installing an extension called Access control Allow Origin from the Chrome web store . And Open the Options or settings page of that extension and type your localhost address in the textbox like this : https://localhost:80 in ur case

I had faced this issue once and this extension worked for me..

Upvotes: 0

Related Questions