Invalid state cookie. An error was encountered while handling the remote login. ASP.NET Core MVC external social login

喜夏-厌秋 提交于 2021-02-16 09:37:06

问题


While implementing external social login in ASP.NET Core2.2 MVC web application without ASP.NET Core Identity. I am getting below error while redirecting back to the application after successful sign in with Google, Facebook, Twitter, LinkedIn and Microsoft.

An unhandled exception occurred while processing the request. Exception: Invalid state cookie. Unknown location

Exception: An error was encountered while handling the remote login. Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler.HandleRequestAsync()

Following are the settings in Startup.cs file

public void ConfigureServices(IServiceCollection services)
   {
       services.Configure<CookiePolicyOptions>(options =>
       {
           // This lambda determines whether user consent for non-essential cookies is needed for a given request.
           options.CheckConsentNeeded = context => true;
           options.MinimumSameSitePolicy = SameSiteMode.None;
       });

       services
           .AddAuthentication(options =>
           {
               options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
           })
           .AddCookie(options =>
           {
               options.Cookie.IsEssential = true;
           })
           .AddGoogle(options =>
           {
               options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               options.ClientId = Configuration["Authentication:Google:ClientId"];
               options.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
               options.CallbackPath = "/externallogincallback";             
           })
           .AddFacebook(facebookOptions =>
           {
               facebookOptions.AppId = Configuration["Authentication:Facebook:AppId"];
               facebookOptions.AppSecret = Configuration["Authentication:Facebook:AppSecret"];
               facebookOptions.CallbackPath = "/externallogincallback";                                        
           })
           .AddLinkedIn(linkedinOptions =>
           {
               linkedinOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               linkedinOptions.ClientId = Configuration["Authentication:LinkedIn:ClientId"];
               linkedinOptions.ClientSecret = Configuration["Authentication:LinkedIn:ClientSecret"];
               linkedinOptions.CallbackPath = "/externallogincallback";                    
           })
           .AddTwitter(twitterOptions =>
           {
               twitterOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               twitterOptions.ConsumerKey = Configuration["Authentication:Twitter:ConsumerKey"];
               twitterOptions.ConsumerSecret = Configuration["Authentication:Twitter:ConsumerSecret"];
               twitterOptions.CallbackPath = "/Home/externallogincallback";                    
           }).AddMicrosoftAccount(microsoftOptions =>
           {
               microsoftOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               microsoftOptions.ClientId = Configuration["Authentication:Microsoft:ClientId"];
               microsoftOptions.ClientSecret = Configuration["Authentication:Microsoft:ClientSecret"];
               microsoftOptions.CallbackPath = "/externallogincallback";
           });
       services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
   }

Following are the details of HomeController.cs (As I am not using Identity so I need to define redirect url specifically.)

      //Action to issue a challange to google login
        public IActionResult LogInMicrosoft(string provider)
        {
            //provider = Microsot or Google or LinkedIn or Twitter or Facebook
            provider = "Microsoft";
            //Issue a challenge to external login middleware to trigger sign in process
            //return new ChallengeResult(provider);

            var authenticationProperties = new AuthenticationProperties
            {
                RedirectUri = Url.Action("externallogincallback")
            };

            return Challenge(authenticationProperties, provider);
            //return new ChallengeResult(provider);
        }

        //Callback action to retrive signin user details
        //public IActionResult externallogincallback(string returnUrl = null, string remoteError = null)\
        public IActionResult externallogincallback()
        {
            //Here we can retrieve the claims
            // read external identity from the temporary cookie
            //var authenticateResult = HttpContext.GetOwinContext().Authentication.AuthenticateAsync("ExternalCookie");
            var result = HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);

            if (result.Result?.Succeeded != true)
            {
                throw new Exception("External authentication error");
            }

            // retrieve claims of the external user
            var externalUser = result.Result.Principal;
            if (externalUser == null)
            {
                throw new Exception("External authentication error");
            }

            // retrieve claims of the external user
            var claims = externalUser.Claims.ToList();

            // try to determine the unique id of the external user - the most common claim type for that are the sub claim and the NameIdentifier
            // depending on the external provider, some other claim type might be used
            //var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject);
            var userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
            if (userIdClaim == null)
            {
                throw new Exception("Unknown userid");
            }

            var externalUserId = userIdClaim.Value;
            var externalProvider = userIdClaim.Issuer;

            // use externalProvider and externalUserId to find your user, or provision a new user

            return RedirectToAction("Privacy", "Home");
        }

回答1:


It seems you want to redirect the request to externallogincallback after sigin the microsoft account. If so, you should not set microsoftOptions.CallbackPath with externallogincallback. With this setting, all the request from Microsoft will be handled by OAuth Middleware instead of your own endpoint externallogincallback.

For redirect page after sign, you need to pass return Challenge(authenticationProperties, provider); by setting the authenticationProperties.authenticationProperties

Follow steps below:

  1. Change the REDIRECT URI in Azure portal with https://localhost:xxx/signin-microsoft
  2. Change Startup.cs with

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    
        public IConfiguration Configuration { get; }
    
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });
    
    
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    
            services.AddAuthentication(options =>
                {
                    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    //options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
                })
                .AddCookie(options =>
                {
                    options.Cookie.IsEssential = true;
                    //options.Cookie.SameSite = SameSiteMode.None;
                })
                .AddMicrosoftAccount(microsoftOptions =>
                {
                    microsoftOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    microsoftOptions.ClientId = Configuration["Authentication:Microsoft:ClientId"];
                    microsoftOptions.ClientSecret = Configuration["Authentication:Microsoft:ClientSecret"];                    
                });
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
    
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseAuthentication();
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
    
  3. HomeController

    public class HomeController : Controller
    {
        //Action to issue a challange to google login
        public IActionResult LogInMicrosoft(string provider)
        {
            //provider = Microsot or Google or LinkedIn or Twitter or Facebook
            provider = "Microsoft";
            var authenticationProperties = new AuthenticationProperties
            {
                RedirectUri = Url.Action("externallogincallback")
            };
            return Challenge(authenticationProperties, provider);
        }
    
        [Route("/[action]")]
        public async Task<IActionResult> externallogincallback()
        {
            var request = HttpContext.Request;
            //Here we can retrieve the claims
            // read external identity from the temporary cookie
            //var authenticateResult = HttpContext.GetOwinContext().Authentication.AuthenticateAsync("ExternalCookie");
            var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    
            if (result.Succeeded != true)
            {
                throw new Exception("External authentication error");
            }
    
            // retrieve claims of the external user
            var externalUser = result.Principal;
            if (externalUser == null)
            {
                throw new Exception("External authentication error");
            }
    
            // retrieve claims of the external user
            var claims = externalUser.Claims.ToList();
    
            // try to determine the unique id of the external user - the most common claim type for that are the sub claim and the NameIdentifier
            // depending on the external provider, some other claim type might be used
            //var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject);
            var userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
            if (userIdClaim == null)
            {
                throw new Exception("Unknown userid");
            }
    
            var externalUserId = userIdClaim.Value;
            var externalProvider = userIdClaim.Issuer;
    
            // use externalProvider and externalUserId to find your user, or provision a new user
    
            return RedirectToAction("Privacy", "Home");
        }
        public IActionResult Index()
        {
            return View();
        }
    
        public IActionResult Privacy()
        {
            return View();
        }
    
        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
    


来源:https://stackoverflow.com/questions/57240285/invalid-state-cookie-an-error-was-encountered-while-handling-the-remote-login

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!