'Getting a SameSite cookie issue when trying to load recaptcha api.js in ASP.Net core project

I am trying to add a Google v2 reCaptcha on my ASP.Net Core 6 Web Application and test it on my localhost. When I add the following line:

<script src="https://www.google.com/recaptcha/api.js" async defer></script>

to my page and open the page I am getting the following error in Chrome's console:

Because a cookie’s SameSite attribute was not set or is invalid, it defaults to SameSite=Lax, which prevents the cookie from being sent in a cross-site request. This behavior protects user data from accidentally leaking to third parties and cross-site request forgery. Resolve this issue by updating the attributes of the cookie: Specify SameSite=None and Secure if the cookie should be sent in cross-site requests. This enables third-party use. Specify SameSite=Strict or SameSite=Lax if the cookie should not be sent in cross-site requests.

All the cookies and request given after are from google.com.

The Captcha is not working and it also prevents some other JS scripts from execution.

If I remove the above line the page loads normally.

Here is how my Program.cs file looks like:

using Microsoft.AspNetCore.Mvc.Infrastructure;
using Serilog;
using Serilog.Events;
using XQ.XGoogleInvisibleCaptcha;

[assembly: System.Runtime.InteropServices.ComVisible(true)]

Log.Logger = new LoggerConfiguration()
          .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
          .Enrich.FromLogContext()
          .WriteTo.File(
                "logs\\Logs.txt",
                rollingInterval: RollingInterval.Day,
                outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}\n")
          .WriteTo.Console(
                outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}\n")
          .CreateLogger();

var lBuilder = WebApplication.CreateBuilder(args);

lBuilder.Host.UseSerilog();


// Build configuration
string lBasePath = Directory.GetParent(AppContext.BaseDirectory) == null ? string.Empty : Directory.GetParent(AppContext.BaseDirectory).FullName;

var lConfiguration = new ConfigurationBuilder()
    .SetBasePath(lBasePath)
    .AddJsonFile("appsettings.json", false)
    .Build();

// Add caching of the static files
lBuilder.Services.AddResponseCaching();

// Add cookies
var cookieOptions = new CookieOptions
{
    // Set the secure flag, which Chrome's changes will require for SameSite none.
    // Note this will also require you to be running on HTTPS.
    Secure = true,

    // Set the cookie to HTTP only which is good practice unless you really do need
    // to access it client side in scripts.
    HttpOnly = true,

    // Add the SameSite attribute, this will emit the attribute with a value of none.
    // To not emit the attribute at all set
    // SameSite = (SameSiteMode)(-1)
    SameSite = SameSiteMode.None,
};

// Use memory cache for the session
lBuilder.Services.AddDistributedMemoryCache();

// Add session to store data
lBuilder.Services.AddSession(options =>
{
    options.Cookie.Name = ".MyWeb.Session";
    options.IdleTimeout = TimeSpan.FromMinutes(2.0);
});

lBuilder.Services.AddCors();

// Add services to the container.
lBuilder.Services.AddSingleton<IConfigurationRoot>(lConfiguration);

// Add the configuration of the Google Invisible Captcha
XGoogleInvisibleCaptchaKeys lGoogleInvisibleCaptchaKeys = lBuilder.Environment.IsDevelopment() ?
    lConfiguration.GetSection("GoogleInvisibleCaptchaDev").Get<XGoogleInvisibleCaptchaKeys>() :
    new(); //

lBuilder.Services.AddSingleton<ICaptchaKeys>(lGoogleInvisibleCaptchaKeys);

// Add the service for obtaining user IP and cookies
lBuilder.Services.AddHttpContextAccessor();

lBuilder.Services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();

// Add services to the container.
lBuilder.Services.AddRazorPages(options =>
{
    options.Conventions.AddPageRoute(
            "/Account/Signup", "Signup");
});

var lApp = lBuilder.Build();

// Configure the HTTP request pipeline.
if (!lApp.Environment.IsDevelopment())
{
    lApp.UseExceptionHandler("/Error");

    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    lApp.UseHsts();
}
else
{
    lApp.UseDeveloperExceptionPage();
}

lApp.UseHttpsRedirection();

lApp.UseStaticFiles();

lApp.UseRouting();

lApp.UseResponseCaching();

// Global CORS policy
lApp.UseCors(x => x
    .SetIsOriginAllowed(origin => true)
    .AllowAnyMethod()
    .AllowAnyHeader()
    .AllowCredentials());

// Global Headers
lApp.Use(async (context, next) =>
{
    context.Response.Headers.Add("X-Developed-By", "Xequtor");
    context.Response.Headers.Add("Cache-Control", "no-store");
    context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
    context.Response.Headers.Add("X-Frame-Options", "DENY");
    context.Response.Headers.Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains; preload");
    context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
    context.Response.Headers.Add("Content-Security-Policy", "form-action 'self'");

    // Cache control
    context.Response.GetTypedHeaders().CacheControl =
        new Microsoft.Net.Http.Headers.CacheControlHeaderValue
        {
            Public = true,
            MaxAge = TimeSpan.FromDays(365),
        };
    context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] =
        new[] { "Accept-Encoding" };

    context.Response.Cookies.Append("MyCookies", "cookieValue", cookieOptions);

    await next();
});

// Redirect to this page when a server error occurs
lApp.UseStatusCodePagesWithReExecute("/Error");

lApp.UseSession();

lApp.UseAuthorization();

lApp.MapRazorPages();

lApp.Run();

From all the post I have seen so far I understood it has to do something on the Google side but might as well be something I am missing. Any ideas?

Thanks.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source