aspnet / Identity

[Archived] ASP.NET Core Identity is the membership system for building ASP.NET Core web applications, including membership, login, and user data. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
1.96k stars 869 forks source link

Customise/Disable the Default UI's Area Name #1815

Closed serpent5 closed 6 years ago

serpent5 commented 6 years ago

Before the Default UI was introduced (pre 2.1), generated projects that included ASP.NET Core Identity were configured to serve Identity-specific endpoints at e.g. /Account/Login. With the new Default UI, this is now e.g. /Identity/Account/Login. Both the existence of an Area and its default name appear to be hardcoded (as IdentityUIDefaultAreaName in the source code) with no option to override.

davidfowl commented 6 years ago

/cc @javiercn

javiercn commented 6 years ago

@serpent5 Can you give us more information on what you're actually trying to accomplish changing the area name/removing the constraint?

Edward-Zhou commented 6 years ago

@javiercn I think I got the same issue with @serpent5 . For the built-in Identity, the request URL should be /Identity/Account/Login, and we want to change the Login address by /Account/Login. But, we did not find anyway to change it.

javiercn commented 6 years ago

@Edward-Zhou What is motivating you to change the area?

Edward-Zhou commented 6 years ago

I just hope one way to control the Identity Razor library request URL.

serpent5 commented 6 years ago

Here are some of the disadvantages of how this currently works, as I see it:

  1. Existing links need to be updated. These might be either internal to the ASP.NET Core Identity web-site or external e.g. if ASP.NET Core Identity is being wrapped up with Identity Server and said external site links to e.g. the /Manage page.
  2. If migrating a site from ASP.NET Identity to ASP.NET Core Identity, it's feasible to make all changes transparent (no visible changes to the end user), unless using the new Default UI, which forces the e.g. /Account/Login link to change.
  3. It further exposes the fact that the site is using ASP.NET Core Identity 2.1, given that /Identity/Account/Login is easily identifiable.
  4. The classic "bookmarks" problem, where existing browser bookmarks to these pages no longer work. This can be fixed with redirects, etc, but that's extra work.

Those last two points are less impactful, but possibly worth mentioning anyway. There’s also the simple reason that some people just might not like the extra token in the url, but maybe they’d just have to suck it up.

I hope this is helpful.

Note: A question was asked on Stack Overflow, which prompted me to look into the source code and consequently raise this issue.

javiercn commented 6 years ago

@serpent5 Thanks for the detailed explanation. I've added some comments inline and I'll give you a couple of options at the end of this comment.

In general, we think of the Default UI as great starting point for new projects and for projects that require a limited amount of customization. If you want to have full control of the UI you can always scaffold the code into your project using the scaffolder that we include. If you are migrating an existing project to ASP.NET Core and want to leverage the UI, you'll need to do some changes when you opt-in into using the default UI.

Existing links need to be updated. These might be either internal to the ASP.NET Core Identity web-site or external e.g. if ASP.NET Core Identity is being wrapped up with Identity Server and said external site links to e.g. the Account/Manage page.

If migrating a site from ASP.NET Identity to ASP.NET Core Identity, it's feasible to make all changes transparent (no visible changes to the end user), unless using the new Default UI, which forces the e.g. /Account/Login link to change.

The classic "bookmarks" problem, where existing browser bookmarks to these pages no longer work. This can be fixed with redirects, etc, but that's extra work.

All these scenarios require you to switch to use the default UI.

It further exposes the fact that the site is using ASP.NET Core Identity 2.1, given that /Identity/Account/Login is easily identifiable.

Can you clarify why this is a concern for you?

Now, if you really want to do this, and keep in mind that this is not a main scenario and not something we support nor recommend with the default UI, you can write a page model convention to change the URLs.

That said, if you choose to do this, there will likely be other places where you need to make adjustments to account for this change.

See https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.applicationmodels.ipageapplicationmodelconvention?view=aspnetcore-2.1

I hope this clarifies things a bit.

BlueMarmalade commented 6 years ago

I don't think this is understood(or maybe i'm the one, haha). The problem for me is that when I tried to implement the new Razor identity into my asp.net core project, I followed the instructions and deleted my old identity before I used the scaffold tool to add the new razor identity. The problem is that now when a user hits an authorized controller like Home/Index he is sent to Account/Login and not Identity/Account/Login. The only fix I could find was putting this in my configure method: app.Use(async (context, next) => { var request = context.Request; if (request.Path == "/Account/Login") { context.Response.Redirect("/Identity/Account/Login"); } else { await next.Invoke(); } });

serpent5 commented 6 years ago

@javiercn Thanks for your feedback.

The identifiability comment was due to the fact that from some of what I read about security, it's sometimes advised to hide the implementation details of your backend services for security reasons. I was just pointing out that a fixed path for the ASP.NET Core Identity Default UI endpoints exposes the fact that ASP.NET Core Identity 2.1 is being used. Whether this is a genuine concern or not is not for me to decide - I just added it to the list of could-be disadvantages.

In conclusion, would it be fair to say that for anyone that wants to remove /Identity from the URLs (or more generally customise said URLs), the recommended options are:

  1. Not use the Default UI at all.
  2. Use the scaffolding element of the Default UI and make all necessary customisations.
  3. Get over it (of course, you can put this far more politely than that :) ).
javiercn commented 6 years ago

@serpent5

The identifiability comment was due to the fact that from some of what I read about security, it's sometimes advised to hide the implementation details of your backend services for security reasons.

This is called security by obscurity and it is generally discouraged as it doesn't provide any real security but just the illusion of security. To draw an analogy, you don't keep your money under your bed for security (based on the fact that you are the only one that knows is under the bed), you put it on a bank where you know that someone has to go though a security guard, crack the bank vault, etc to get to your money.

In conclusion, would it be fair to say that for anyone that wants to remove /Identity from the URLs (or more generally customise said URLs), the recommended options are:

Not use the Default UI at all. Use the scaffolding element of the Default UI and make all necessary customisations. Get over it (of course, you can put this far more politely than that :) ).

Yes, but I also would add to the explanation that the main reason for the Identity UI to be in an area is to minimize the impact on your app and to provide a clean separation between your app code and the Identity code. Also, as you've mentioned before, if you want to leverage the default UI and still use the old routes its totally acceptable to just have a redirection rule that points the old routes to the new routes. (It's essentially something you can achieve with 5 lines of code).

javiercn commented 6 years ago

@BlueMarmalade

I followed the instructions and deleted my old identity before I used the scaffold tool to add the new razor identity.

Can you point me to those instructions? If they are in our docs, I don't think they are accurate and we should get them fixed.

The problem is that now when a user hits an authorized controller like Home/Index he is sent to Account/Login and not Identity/Account/Login. The only fix I could find was putting this in my configure method:

Call services.AddIdentity().AddDefaultUI() or just services.AddDefaultIdentity instead. (Or just configure the identity cookie to redirect to /Identity/Account/Login

serpent5 commented 6 years ago

@javiercn

To draw an analogy, you don't keep your money under your bed for security (based on the fact that you are the only one that knows is under the bed), you put it on a bank where you know that someone has to go though a security guard, crack the bank vault, etc to get to your money.

This is a good example of security by obscurity, but I don't think it lines up with what we're talking about here. With your banking example, you've given up all the security provided by the bank by taking it entirely into your own hands. However, with the URL point I made, we've not given up all the existing security layers already available in the stack providing ASP.NET Core Identity. I guess it's more like putting all your money into a bank and then not disclosing which bank it is - You get all the security of the bank but have made it slightly harder for someone to know which bank.

This is going a bit off-topic now - I don't think we need to debate security by obscurity any further. Thanks for your feedback and comments and for taking the time to respond.

serpent5 commented 6 years ago

Here's the URL for the docs mentioned by @BlueMarmalade: https://docs.microsoft.com/en-us/aspnet/core/migration/20_21?view=aspnetcore-2.1#changes-to-authentication-code.

javiercn commented 6 years ago

@serpent5 Thanks for pointing out the docs. @Rick-Anderson Can you please correct the migration docs to point out that when upgrading to 2.1 your urls will change? I think the migration strategy there might be a bit naive and that we might need to provide more detailed migration steps.

BlueMarmalade commented 6 years ago

Actually, instead of using my old db context I now created a new one just for the new identity, I had no problem with the routing this time. In fact it says to create a new one in the migration if you add razor identity to an mvc app without existing identity. But it is quick to ignore this because 1) I already used a database in my app and 2) I deleted all my old identity code before adding the new razor identity so I mistakenly follewed the instructions on how to add razor identity in mvc projects with existing authorization(since i deleted it i should have followed the instructions on adding in mvc app widthout existing authorization). A bit confusing a messy on my part, but quick to do.

UnknownQuestions commented 6 years ago

If you want to map from Identity/Account to /Account, try out following code: services.AddMvc() .AddRazorPagesOptions(o => o.Conventions.AddAreaFolderRouteModelConvention("Identity", "/Account/", model => { foreach (var selector in model.Selectors) { var attributeRouteModel = selector.AttributeRouteModel; attributeRouteModel.Order = -1; attributeRouteModel.Template = attributeRouteModel.Template.Remove(0, "Identity".Length); } }); }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

javiercn commented 6 years ago

Thank you for your feedback. We think this has been addressed with the updated migration docs in https://github.com/aspnet/Docs/issues/6953. For now, we're closing this issue.

tmutton commented 5 years ago

If you want to map from Identity/Account to /Account, try out following code: services.AddMvc() .AddRazorPagesOptions(o => o.Conventions.AddAreaFolderRouteModelConvention("Identity", "/Account/", model => { foreach (var selector in model.Selectors) { var attributeRouteModel = selector.AttributeRouteModel; attributeRouteModel.Order = -1; attributeRouteModel.Template = attributeRouteModel.Template.Remove(0, "Identity".Length); } }); }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

This works but you will find the Login redirects will not work. To fix this one option is to add a controller called IdentityController with the following action:

[Route("Identity/Account/Login")]
public IActionResult LoginRedirect(string ReturnUrl)
{
    return Redirect("/Account/Login?ReturnUrl=" + ReturnUrl);
}

A bit hacky but it works

seraphx2 commented 5 years ago

I say merely for looks this should be officially changed. "Identity" is not terminology that users understand and it just looks tacky in front of the rest of the route. It means nothing except to us developers and makes for unnecessarily longer and odd looking URLs by enforcing an area. And it had never been like this in the past several years of MVC.

md-waldron commented 5 years ago

I am adding Identity to an IdentityServer project. There is no requirement for this app to seperate the identity part out by using an area as all it does is auth and identity based stuff, no seperation is required. I would like an option to be able to remove the '/Identity' part of the routes for this project as its adding something which could potentially be distrating for my users when they are logging in. I want to keep the routes as clean as possible.