Reputation: 97
This is a weird one. Both TempData and Session work fine for the most part, but, if you access the website via a link from within Outlook Web (the link must be within an HTML email), then both TempData and Session lose their values after redirection.
As an example, create a new ASP.NET Core MVC Project with Visual Studio 2022, running on .NET Core 8, and replace the Program.cs and HomeController.cs with the following. Also add the Test.cshtml view. Then try building and running this code (built with Visual Studio 2022, running on .NET Core 8).
It will run fine. But then send yourself an HTML formatted email, with a link to the website embedded (running on localhost) and see what happens. When you click on that link and hit the site, both TempData and Session will be null after the redirection.
HomeController.cs
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using WebApplication10.Models;
namespace WebApplication10.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return RedirectToAction("One");
}
public IActionResult One()
{
return RedirectToAction("Two");
}
public IActionResult Two()
{
CookieOptions cookieOptions = new CookieOptions()
{
Path = "/",
Expires = DateTime.Now.AddSeconds(30),
Secure = true,
HttpOnly = true
};
Response.Cookies.Append("Oatmeal", "877", cookieOptions);
HttpContext.Session.SetInt32("USERID", 877);
TempData["TEST"] = "HELLO";
return RedirectToAction("Test");
}
public IActionResult Test()
{
var cookie = Request.Cookies["Oatmeal"];
TempData["USERID"] = HttpContext.Session.GetInt32("USERID");
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
Program.cs
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.CookiePolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Globalization;
using WebApplication10.Data;
namespace WebApplication10
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequiredLength = 6;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequiredUniqueChars = 3;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
options.User.RequireUniqueEmail = true;
// Sign-in settings
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedPhoneNumber = false;
options.SignIn.RequireConfirmedAccount = false;
});
builder.Services.ConfigureApplicationCookie(options =>
{
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.LoginPath = "/Identity/Account/Login";
options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
options.ExpireTimeSpan = TimeSpan.FromDays(150);
options.SlidingExpiration = true;
options.Cookie = new CookieBuilder
{
HttpOnly = true,
Path = "/",
Name = "TestAuthCookie",
SecurePolicy = CookieSecurePolicy.Always,
IsEssential = true,
SameSite = SameSiteMode.Strict
};
});
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeAreaFolder("Identity", "/Manage");
});
builder.Services.AddServerSideBlazor();
builder.Services.AddDistributedMemoryCache(options =>
{
options.ExpirationScanFrequency = TimeSpan.FromMinutes(5);
options.SizeLimit = 100;
options.CompactionPercentage = 0.2;
});
builder.Services.AddResponseCaching();
builder.Services.Configure<CookiePolicyOptions>(options =>
{
options.HttpOnly = HttpOnlyPolicy.Always;
options.Secure = CookieSecurePolicy.Always;
options.MinimumSameSitePolicy = SameSiteMode.Strict;
options.CheckConsentNeeded = context => false;
});
builder.Services.Configure<CookieTempDataProviderOptions>(options =>
{
options.Cookie.Name = "TEMPDATA";
options.Cookie.IsEssential = true;
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.HttpOnly = true;
});
builder.Services.AddSession(options =>
{
options.Cookie.Name = "TESTSESSION";
options.IdleTimeout = TimeSpan.FromMinutes(15);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
builder.WebHost.UseStaticWebAssets();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.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.UseResponseCaching();
app.UseRouting();
CookiePolicyOptions cookiePolicy = new CookiePolicyOptions
{
HttpOnly = HttpOnlyPolicy.Always,
Secure = CookieSecurePolicy.Always,
MinimumSameSitePolicy = SameSiteMode.Strict,
CheckConsentNeeded = context => false
};
app.UseCookiePolicy(cookiePolicy);
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
var supportedCultures = new[]
{
new CultureInfo("en-US")
};
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en-US"),
// Formatting numbers, dates, etc.
SupportedCultures = supportedCultures,
// UI strings that we have localized.
SupportedUICultures = supportedCultures
});
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();
app.MapBlazorHub();
app.Run();
}
}
}
Views/Home/Test.cshtml
<p>@TempData["TEST"]</p>
<p>@TempData["USERID"]</p>
Upvotes: 0
Views: 84
Reputation: 97
Turns out this is all due to SameSiteMode. When it is set to Strict, everything breaks when loading the site via a link clicked on from an external application. I had to set all my cookies to SameSiteMode.Lax.
Upvotes: 0