Attribute routing - show logged in user id in the url

443 views Asked by At

I am using MVC custom authentication in my project and I am clueless about how do I decorate my URL as per my requirement. Basically what I need is, whenever the user is authenticated and gains access to my application he is redirected to the main route mapped in rote config which is something like this:

localhost:64843/dashboard

What I need is, suppose Janet is logging in and has passed authentication then I want the landing page's url to look like:

localhost:64843/Janet/dashboard

Note: The username field (i.e. Janet in this context is generated by adding a custom field to AspNetRoles table)

And also I need the URL structure to stay like that when I browse other pages. For example I am on create view of products page then the URL should be like :

localhost:64843/Janet/Products/Create

And this structure should be for all users whoever logs in according to their username.

I am new to attribute routing principle and any help on this would be highly appreciated.

Route config

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Username_Default",
                url: "{username}/{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                constraints: new { username = new OwinUsernameConstraint() }
            );


            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }

Username constraint

    public class OwinUsernameConstraint : IRouteConstraint
    {
        private object synclock = new object();

        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        {
            if (parameterName == null)
                throw new ArgumentNullException("parameterName");
            if (values == null)
                throw new ArgumentNullException("values");

            object value;
            if (values.TryGetValue(parameterName, out value) && value != null)
            {
                string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
                return this.GetUsernameList(httpContext).Contains(valueString);
            }
            return false;
        }

        private IEnumerable<string> GetUsernameList(HttpContextBase httpContext)
        {
            string key = "UsernameConstraint.GetUsernameList";
            var usernames = httpContext.Cache[key];
            if (usernames == null)
            {
                lock (synclock)
                {
                    usernames = httpContext.Cache[key];
                    if (usernames == null)
                    {
                        // Retrieve the list of usernames from the database
                        using (var db = ApplicationDbContext.Create())
                        {
                            usernames = (from users in db.Users
                                            select users.UserName).ToList();
                        }

                        httpContext.Cache.Insert(
                            key: key,
                            value: usernames,
                            dependencies: null,
                            absoluteExpiration: Cache.NoAbsoluteExpiration,
                            slidingExpiration: TimeSpan.FromSeconds(15),
                            priority: CacheItemPriority.NotRemovable,
                            onRemoveCallback: null);
                    }
                }
            }

            return (IEnumerable<string>)usernames;
        }
    }

Register controller

        public async Task<ActionResult> Register(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = new ApplicationUser { UserName = model.UserName, Email = model.Email };
                var result = await UserManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);

                    // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
                    // Send an email with this link
                    // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
                    // var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
                    // await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");

                    return RedirectToAction("Index","Home");
                }
                AddErrors(result);
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

Thanks

1

There are 1 answers

3
Nick Lam On

You can add custom route after the user logged in successfully:

case SignInStatus.Success:
                using (RouteTable.Routes.GetWriteLock())
                {
                    RouteTable.Routes.Insert(0, new Route("{user}/{controller}/{action}/{id}", 
                        new RouteValueDictionary() { ["user"] = "Nick", ["controller"] = "Home", ["action"] = "Index", ["id"] = UrlParameter.Optional }, 
                        new MvcRouteHandler()));
                }
                return RedirectToLocal(returnUrl);

And remove it when the user log out:

AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
        using (RouteTable.Routes.GetWriteLock())
        {
            RouteTable.Routes.RemoveAt(0);
        }
        return RedirectToAction("Index", "Home");