0

In my net core 5 application i need to set two login path, one for administrators in admin area and other one will be for users area, i have configured application cookie but i cant access the current url in ConfigureServices so i cant change the login path depends on the url

My code

public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration _config)
{
    AppIdentitySettings appIdentitySettings = _config.GetSection("AppIdentitySettings").Get<AppIdentitySettings>();
    services.AddIdentity<CI_User, CI_Role>(opt =>
    {
        //password settings
        opt.Password.RequiredLength = appIdentitySettings.Password.RequiredLength;
        opt.Password.RequireDigit = appIdentitySettings.Password.RequireDigit;
        opt.Password.RequiredUniqueChars = appIdentitySettings.Password.RequiredUniqueChars;
        opt.Password.RequireUppercase = appIdentitySettings.Password.RequireUppercase;
        opt.Password.RequireLowercase = appIdentitySettings.Password.RequireLowercase;
        opt.Password.RequireNonAlphanumeric = appIdentitySettings.Password.RequireNonAlphanumeric;
        //user settings
        //opt.User.AllowedUserNameCharacters = "ghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP";
        opt.User.RequireUniqueEmail = appIdentitySettings.User.RequireUniqueEmail;
        //Lockout settings
        //opt.Lockout.AllowedForNewUsers = false;
    })
    .AddEntityFrameworkStores<_ModelsContext>()
    .AddDefaultTokenProviders();

    //configure cookie
    services.ConfigureApplicationCookie(opt =>
    {
        // Cookie settings
        opt.Cookie.HttpOnly = true;
        opt.ExpireTimeSpan = TimeSpan.FromMinutes(60);

        opt.LoginPath = "/ar/Home/App";
        opt.AccessDeniedPath = "/Identity/Account/AccessDenied";
        opt.SlidingExpiration = true;
    });

    return services;
}

How can i do this situation !?

----------UPDATE---------

I have found two solutions, or you can call it a workaround

Solution one : add two different Authentication scheme in start up ConfigureServices

string defaultCulture = _config.GetValue<string>("DefaultCulture");
services.AddAuthentication(opt => { opt.DefaultScheme = "UserAuth"; })
    .AddCookie("UserAuth", opt =>
    {
        opt.LoginPath = $"/{defaultCulture}/User/Login";
        opt.AccessDeniedPath = $"/{defaultCulture}/Account/AccessDenied/";
    })
    .AddCookie("AdminAuth", opt =>
    {
        opt.LoginPath = $"/{defaultCulture}/Admin/About";
        opt.AccessDeniedPath = $"/{defaultCulture}/Admin/Account/AccessDenied/";
    });

And for usage

[Authorize(AuthenticationSchemes = "AdminAuth")]

Problems with solution one : If you have a multi language application, you cannot redirect the user dynamically to his chosen culture

Solution two : add custom authorization attribute

public class CustomAuthorizeAttribute : Attribute, IAuthorizationFilter
{
    public bool IsAdmin { get; set; } = false;

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        bool hasAllowAnonymous = context.ActionDescriptor.EndpointMetadata.Any(em => em.GetType() == typeof(AllowAnonymousAttribute));
        bool isAuth = context.HttpContext.User.Identity.IsAuthenticated;

        if (!isAuth && !hasAllowAnonymous)
        {
            string redirectUrl = context.HttpContext.Request.Path.Value;

            if (IsAdmin)
                context.Result = new RedirectToActionResult("Index", "About", new { redirectUrl = redirectUrl, area = "Admin" });
            else
                context.Result = new RedirectToActionResult("App", "Home", new { redirectUrl = redirectUrl });
        }
    }
}

Problems with solution two : You cannot do the same logic if you inherited from Authorize attribute, so you will lose all of authorize attribute benefits !

Ahmad Alaa
  • 767
  • 9
  • 30
  • See this [thread](https://stackoverflow.com/questions/48032216/implement-different-login-page-for-each-role-in-asp-net-core-2) may helpful. – Yinqiu Mar 12 '21 at 02:58
  • I have updated my question – Ahmad Alaa Mar 15 '21 at 13:38
  • Can't you just do it the other way around? Admins/users both log in via the same route, but get different claims according to whether they're admins or not. Then just restrict access and visibility of the admin area to those with the admin claim? – Nat Wallbank Mar 15 '21 at 14:18
  • We can do that if the admin and user areas has the same design, and the website has credentials for users also, but we are building cms, the dashboard will has its own design and its login link will not visible to users – Ahmad Alaa Mar 16 '21 at 10:41

2 Answers2

1

I use this solution to have extra login page in admin area:

services.ConfigureApplicationCookie(options =>
{
  options.Events = new CookieAuthenticationEvents
  {
    OnRedirectToLogin = redirectContext =>
    {
        // Area's own login page

        const string area = "/Admin";

        if (redirectContext.Request.Path.StartsWithSegments(area))
        {
            var uriBuilder = new UriBuilder(redirectContext.RedirectUri);

            uriBuilder.Path = area + uriBuilder.Path;

            redirectContext.RedirectUri = uriBuilder.ToString();
        }

        return Task.CompletedTask;
    }
};

});

This is the easiest way I found after playing with authentication schemes.

Saly
  • 51
  • 4
-1
if (_env.IsDevelopment())
{
    services.ConfigureApplicationCookie(options =>
    {
        options.LoginPath = "/Account/AdminLogin";
    });
}
else
{
    services.ConfigureApplicationCookie(options =>
    {
        options.LoginPath = "/Account/Login";
    });
}

[AllowAnonymous]
[Route("Account")]
public class AccountController : Controller
{

    [Route("Login")]
    public IActionResult Login()
    {
        return Redirect("Your login path");
    }

    [Route("AdminLogin")]
    public async Task<IActionResult> AdminLogin()
    {
        if (_env.IsDevelopment())
            await _signInManager.PasswordSignInAsync("admin user name", "admin password", true, false);
        return Redirect("Your login path");
    }
}