I am facing a problem when using systemweb adapters and YARP to incrementally migrate from ASP.NET on .NET 4.8 to ASP.NET Core 8.0.
Specifically I am using authentication client and server and when I browse the home page before authentication on a mobile device, the request URL becomes too long due to recursive addition of ReturnUrls to it.
The URL looks like this:
http://localhost:8080/account/login?ReturnUrl=%2fsystemweb-adapters%2fauthenticate%3foriginal-url%3d%252Faccount%252Flogin%253FReturnUrl%253D%25252fsystemweb-adapters%25252fauthenticate%25253foriginal-url%25253d%2525252Faccount%2525252Flogin%2525253FReturnUrl%2525253D%252525252fsystemweb-adapters%252525252fauthenticate%252525253foriginal-url%252525253d%25252525252Faccount%25252525252Flogin%25252525253FReturnUrl%25252525253D%2525252525252fsystemweb-adapters%2525252525252fauthenticate%2525252525253foriginal-url%2525252525253d%252525252525252F%252525252526original-url%25252525253D%2525252525252F%25252525252C%25252525252Faccount%25252525252Flogin%25252525253FReturnUrl%25252525253D%2525252525252f%25252526original-url%2525253D%252525252Faccount%252525252Flogin%252525253FReturnUrl%252525253D%25252525252fsystemweb-adapters%25252525252fauthenticate%25252525253foriginal-url%25252525253d%2525252525252F%2525252526original-url%252525253D%25252525252F%252525252C%252525252Faccount%252525252Flogin%252525253FReturnUrl%252525253D%25252525252f%2525252C%2525252Fmobile%2525252Flogin%2526original-url%253D%25252Faccount%25252Flogin%25253FReturnUrl%25253D%2525252fsystemweb-adapters%2525252fauthenticate%2525253foriginal-url%2525253d%252525252Faccount%252525252Flogin%252525253FReturnUrl%252525253D%25252525252fsystemweb-adapters%25252525252fauthenticate%25252525253foriginal-url%25252525253d%2525252525252F%2525252526original-url%252525253D%25252525252F%252525252C%252525252Faccount%252525252Flogin%252525253FReturnUrl%252525253D%25252525252f%252526original-url%25253D%2525252Faccount%2525252Flogin%2525253FReturnUrl%2525253D%252525252fsystemweb-adapters%252525252fauthenticate%252525253foriginal-url%252525253d%25252525252F%25252526original-url%2525253D%252525252F%2525252C%2525252Faccount%2525252Flogin%2525253FReturnUrl%2525253D%252525252f%25252C%25252Fmobile%25252Flogin%252C%252Fmobile%252Flogin&original-url=%2Faccount%2Flogin%3FReturnUrl%3D%252fsystemweb-adapters%252fauthenticate%253foriginal-url%253d%25252Faccount%25252Flogin%25253FReturnUrl%25253D%2525252fsystemweb-adapters%2525252fauthenticate%2525253foriginal-url%2525253d%252525252Faccount%252525252Flogin%252525253FReturnUrl%252525253D%25252525252fsystemweb-adapters%25252525252fauthenticate%25252525253foriginal-url%25252525253d%2525252525252F%2525252526original-url%252525253D%25252525252F%252525252C%252525252Faccount%252525252Flogin%252525253FReturnUrl%252525253D%25252525252f%252526original-url%25253D%2525252Faccount%2525252Flogin%2525253FReturnUrl%2525253D%252525252fsystemweb-adapters%252525252fauthenticate%252525253foriginal-url%252525253d%25252525252F%25252526original-url%2525253D%252525252F%2525252C%2525252Faccount%2525252Flogin%2525253FReturnUrl%2525253D%252525252f%25252C%25252Fmobile%25252Flogin%26original-url%3D%252Faccount%252Flogin%253FReturnUrl%253D%25252fsystemweb-adapters%25252fauthenticate%25253foriginal-url%25253d%2525252Faccount%2525252Flogin%2525253FReturnUrl%2525253D%252525252fsystemweb-adapters%252525252fauthenticate%252525253foriginal-url%252525253d%25252525252F%25252526original-url%2525253D%252525252F%2525252C%2525252Faccount%2525252Flogin%2525253FReturnUrl%2525253D%252525252f%2526original-url%253D%25252Faccount%25252Flogin%25253FReturnUrl%25253D%2525252fsystemweb-adapters%2525252fauthenticate%2525253foriginal-url%2525253d%252525252F%252526original-url%25253D%2525252F%25252C%25252Faccount%25252Flogin%25253FReturnUrl%25253D%2525252f%252C%252Fmobile%252Flogin%2C%2Fmobile%2Flogin
This is the code on the ASP.NET Core side:
using WebCore;
using Yarp.ReverseProxy.Forwarder;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddSystemWebAdapters()
.AddJsonSessionSerializer(options =>
{
// <snip>
})
.AddRemoteAppClient( options =>
{
options.RemoteAppUrl = new(builder.Configuration["ProxyTo"]);
options.ApiKey = builder.Configuration["ProxyApiKey"];
})
.AddAuthenticationClient(isDefaultScheme: true)
.AddSessionClient();
builder.Services.AddHttpForwarder();
builder.Services.AddDetection();
builder.Services.AddWebOptimizer(//<snip>);
// <snip>
var app = builder.Build();
var forwarderRequestConfig = builder.Configuration.GetSection("ForwarderRequestConfig")
.Get<ForwarderRequestConfig>();
if (!app.Environment.IsDevelopment())
{
app.UseHsts();
}
app.UseCors();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseSystemWebAdapters();
app.MapForwarder("/{**catch-all}", app.Configuration["ProxyTo"], forwarderRequestConfig, new ForwardOriginalUrlTransformer())
.Add(static builder => ((RouteEndpointBuilder)builder).Order = int.MaxValue);
app.MapDefaultControllerRoute()
.RequireSystemWebAdapterSession();
app.Run();
On the ASP.NET side, I have an AccountsController
for login that looks like this:
[Mobility]
public ActionResult Login(string returnUrl = "", string mode = "")
{
SecurityService.Logout();
if (returnUrl.Contains("/account/login"))
return RedirectToAction("login", "account", new { mode = "login" });
var model = new
{
Mode = mode,
RedirectUrl = returnUrl,
ValidationLiterals = AccountFacade.ValidationLiterals()
};
return View(model.ToJson());
}
And the Mobility
attribute is coded as:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Determine if we've processed their preference already via the UI site-switching links
var useMobileValue = HttpContext.Current.Session["UseMobile"];
bool useMobile;
// <snip>
if (useMobile)
{
var url = "/mobile";
if (!Global.Authenticated)
url += "/login";
filterContext.Result = new RedirectResult(url);
}
}
Thanks for your time.
I was expecting this work by default but it doesn't. I have tried adding a middleware to strip off extra ReturnUrls on the ASP.NET Core side, but it is getting more and more complicated as I code it and it is not helping.
I am wondering if the multiple redirects is causing problems with the auth flow. Is there a work around for this? Will it help me if I migrate the home and accounts controller over to ASP.NET Core, but in this case, how do I set the login path?
I fixed this using the following code
public override async ValueTask<bool> TransformResponseAsync(HttpContext context, HttpResponseMessage proxyResponse)
{
// Allow redirect response to pass through
if (proxyResponse?.StatusCode == HttpStatusCode.Found &&
proxyResponse?.Headers?.Location?.ToString()?.EndsWith("/mobile/login", StringComparison.OrdinalIgnoreCase) == true)
{
context.Response.StatusCode = (int)proxyResponse.StatusCode;
context.Response.Headers.Location = proxyResponse.Headers.Location?.ToString();
return false; // Prevent YARP from processing the redirect
}
// Allow default processing for other responses
return await HttpTransformer.Default.TransformResponseAsync(context, proxyResponse);
}