Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Core 2.0 Cookie Events OnRedirectToLogin

How can I apply a middleware to the OnRedirectToLogin event in Cookie options (so i can use dependency injection), OR how can I retrieve actionContext from RedirectContext? I have tried searching for documentation or examples, but it is hard to find, and I have not seen any examples explaining or defining how. Is this even possible?

my Startup.cs

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(o =>
                {
                    o.AccessDeniedPath = new PathString("/Error/AccessDenied");
                    o.LoginPath = new PathString("/Account/Login/");
                    o.Cookie.Path = "/";
                    o.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
                    o.Cookie.HttpOnly = true;
                    o.LogoutPath = new PathString("/Account/Logout/");
                    o.Events.OnRedirectToLogin = (context) =>
                    {
                        var routeData = context.HttpContext.GetRouteData();
                        RouteValueDictionary routeValues = new RouteValueDictionary();
                        if (routeData != null) routeValues.Add("lang", routeData.Values["lang"]);
                        Uri uri = new Uri(context.RedirectUri);
                        string returnUrl = HttpUtility.ParseQueryString(uri.Query)[context.Options.ReturnUrlParameter];
                        string focustab = "";
                        context.Request.Query.ToList().ForEach(x =>
                        {
                            if (x.Key == "id") routeValues.Add("id", x.Value.FirstOrDefault());
                            if (x.Key == "values") routeValues.Add("values", x.Value.FirstOrDefault());

                        });

                        routeValues.Add(context.Options.ReturnUrlParameter, returnUrl + focustab);

                        //context here is a redirect context, how can i get the action context to create a new Action as what UrlHelper is expecting
                        context.RedirectUri = new UrlHelper(context).Action("login", "account", routeValues);
                        return Task.CompletedTask;
                    };
                });

Thanks in advance.

like image 315
John Dover Avatar asked Feb 01 '18 17:02

John Dover


People also ask

What is CookieAuthenticationDefaults AuthenticationScheme?

The CookieAuthenticationDefaults. AuthenticationScheme GitHub Source shows it's set to "Cookies" . The authentication cookie's IsEssential property is set to true by default. Authentication cookies are allowed when a site visitor hasn't consented to data collection.

What is ASP.NET Core session cookie?

Session uses a cookie to track and identify requests from a single browser. By default, this cookie is named . AspNetCore. Session , and it uses a path of / . Because the cookie default doesn't specify a domain, it isn't made available to the client-side script on the page (because HttpOnly defaults to true ).

Where are cookies stored in .NET Core?

Introduction. HTTP Cookie is some piece of data which is stored in the user's browser.

Does ASP.NET Core identity use cookies?

ASP.NET Core provides a cookie authentication mechanism which on login serializes the user details in form of claims into an encrypted cookie and then sends this cookie back to the server on subsequent requests which gets validated to recreate the user object from claims and sets this user object in the HttpContext so ...


1 Answers

I was able to get it working as I wanted, so I am posting my answer in case anyone else comes across this question. With the help of this documentation: Use cookie authentication without ASP.NET Core Identity. I implemented the CustomCookieAuthenticationEvents and casted that as the type for the Cookie events.

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(o =>
    {
        o.AccessDeniedPath = new PathString("/Error/AccessDenied");
        o.LoginPath = new PathString("/Account/Login/");
        o.Cookie.Path = "/";
        o.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
        o.Cookie.HttpOnly = true;
        o.LogoutPath = new PathString("/Account/Logout/");
        //This line here
        o.EventsType = typeof(CustomCookieAuthenticationEvents);
    });

//register the IActionContextAccessor so that I can inject into my CustomCookieAuthenticationEvents constructor
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();

Then inside that Custom class, I was able to inject IUrlHelperFactory and IActionContextAccessor which helps me create new UrlHelper for the current action.

public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
    private IUrlHelperFactory _helper;
    private IActionContextAccessor _accessor;
    public CustomCookieAuthenticationEvents(IUrlHelperFactory helper, IActionContextAccessor accessor)
    {
        _helper = helper;
        _accessor = accessor;
    }
    public override Task RedirectToLogin(RedirectContext<CookieAuthenticationOptions> context)
    {
        var routeData = context.Request.HttpContext.GetRouteData();
        RouteValueDictionary routeValues = new RouteValueDictionary();
        //Create new routeValues based on routeData above
        ... code removed for brevity
        Uri uri = new Uri(context.RedirectUri);
        string returnUrl = HttpUtility.ParseQueryString(uri.Query)[context.Options.ReturnUrlParameter];

        routeValues.Add(context.Options.ReturnUrlParameter, returnUrl + focustab);
        var urlHelper = _helper.GetUrlHelper(_accessor.ActionContext);
        context.RedirectUri = UrlHelperExtensions.Action(urlHelper, "login", "account", routeValues);
        return base.RedirectToLogin(context);
    }
}

This allows me to modify the query string and returnUrl parameter on Redirect to Login requests. My requirement was to remove a specific query string value on first request to Login, and this allows me to meet that need.

It was pointed out in the comments that ActionContext is null in .net Core 3.1. I created a new .net core 3.1 project and this is the new code to make sure ActionContext is NOT null. Have not fully tested, just made sure the ActionContext was not null for inject IActionContextAccessor.

The main thing to remember here is that IActionContextAccessor works with the MVC pipeline. This means we would have to change the .net core 3.1 template to use MVC. CustomCookieAuthenticationEvents does not change.

New Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc(m =>
        {
            m.EnableEndpointRouting = false;
        });
        //register the IActionContextAccessor so that I can inject into my CustomCookieAuthenticationEvents constructor
        services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
        services.AddScoped<CustomCookieAuthenticationEvents>();
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie(o =>
        {
            o.AccessDeniedPath = new PathString("/Error/AccessDenied");
            o.LoginPath = new PathString("/Account/Login/");
            o.Cookie.Path = "/";
            o.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
            o.Cookie.HttpOnly = true;
            o.LogoutPath = new PathString("/Account/Logout/");
            //This line here
            o.EventsType = typeof(CustomCookieAuthenticationEvents);
        });

    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment 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.UseRouting();

        app.UseAuthorization();

        app.UseMvcWithDefaultRoute();

        //app.UseEndpoints(endpoints =>
        //{
        //    endpoints.MapControllerRoute(
        //        name: "default",
        //        pattern: "{controller=Home}/{action=Index}/{id?}");
        //});
    }
like image 199
John Dover Avatar answered Jan 04 '23 11:01

John Dover



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!