Multi-site, single code-base user registration using ASPNet.Identity - c#

So I have multiple websites running off one code base (asp.net standard MVC). I'm using the built in ASPNet.Identity methods for users to register/log in (using ApplicationSignInManager, ApplicationUserManager).
Currently, all websites are using a single database to store user information. This is causing a couple of issues:
When user A registers on website A, they are now able to log into website B with the same details. As far as they are aware, they did not register on website B. Not good!
If I constrain user A to only access website A, if that user then tried to register on website B, they get the 'email address already in use' error. Also not good!
I've tried separating the databases, one per site, to get around this issue but I don't know how to dynamically change the DBContext assigned to the ApplicationSignInManager and ApplicationUserManager in my controller.
For example, when a user comes to website A, I grab the connection string for website A and perform login/register actions. I need the domain name to work out which connection string to load, which I can't access until after startup.cs code has run, configuring my manager instances.
I figure other people must have done this. Ideally I need to dynamically change the DBContext AFTER Startup.cs has run. Failing that, I need a nice approach to storing multiple identical email addresses in the same DB
Thanks

If you have a flag somewhere which website the current context is for, i'd say easiest to achieve that is to do two things:
extend the IdentityUser with a Website property, something simple either just an int WebsiteId or a String.
extend the AccountController to use that property wherever needed, I think you'd need to modify "Register" and all "Login" functions to verify the website the account is for.

I managed to find a solution. It's not the most elegant but it solves the issue until I can figure out how to dynamically change the DB Context used during login/register
In IdentityConfig.cs I switched 'RequireUniqueEmail' to false:
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = false
};
Then when a user registers, I take the email address, make it unique to the website they registered on, and store it in the UserName field (I don't use the UserName for anything). When they log in, I make the same alteration to the entered email address before attempting login.
If this user registers on a different website, the username will be different even though the email is identical. Not perfect, but it works.
E.g.
string uniqueCode = "Website_Specific_String";
var user = new ApplicationUser { uniqueCode + model.Email, Email = model.Email};
var result = await UserManager.CreateAsync(user, model.Password);
and the login
var user = await UserManager.FindByNameAsync(uniqueCode + model.Email);
Still open to better ideas. Thanks

Related

Asp.net core 5, using Identity, how do I change the default redirect of the [Authorize] Attribute?

The [Authorize] is wonderful for locking pages down but I am building a new product with few users and it makes no sense that it directs people to Login, because there is no one to login yet. It should direct them to Register instead.
But I am struggling to find an easy way to do that without a ton of middleware.
You can change the LoginPath on start up, but I suspect this does not answer your question because when enough users exists then what happens?
To change the login path you can add:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(cookieOptions =>
{
cookieOptions.LoginPath = "/register";
cookieOptions.AccessDeniedPath = "/account/denied";
cookieOptions.ExpireTimeSpan = TimeSpan.FromMinutes(120);
});
However if you are wanting a switch when you have reached a critical mass then this will not suffice.
EDIT one approach could be something like:
Create a loginOrRegister page. Then on this page hit the database (or whatever you use to see whether you have hit critical mass or its a known user based on a cookie) and then either
Redirect to Login
OR
Rediect to register

How to check current user's role in ASP.NET MVC 5

currently dealing with an interesting problem. I am building a website with three different user roles. When logged in, the MVC partial view shows navigation options for the users. I want to show different options depending on the user's role. In previous websites, I have used the following code to determine a role:
#if (Roles.IsUserInRole("intern"))
{
<li>#Html.ActionLink("Log Time", "Index", "Time")</li>
}
Unfortunately, when I attempted this in my current code, I got the message:
The Role Manager feature has not been enabled.
So apparently in the new MVC they disable the role manager by default and have a new way of doing it. No biggie. Searching the issue suggested that I enable the feature in web.config. I followed several instructions on how to do that (I promise I can google search) but it seems to mess with my SQL Server connection string, giving me errors that indicate it's trying to log in to a local db that doesn't exist rather than my Azure SQL Server. I've played around for a while and I don't know why this is the case.
Anyway, long story short, rather than work around and re-enable a vestigial Identity feature, how are you supposed to accomplish this in the new MVC? I can get the roles fine controller side with user manager, but I can't use that in a view. Similarly a Viewbag full of roles can't work because this is navigation on every page.
I appreciate all the help in advance, thanks everyone!
Just found the answer, I'll leave this up for other people dealing with this. The correct way to do this is:
#if (User.IsInRole("intern"))
This makes sense since MVC is moving away from Role based objects and towards User based objects.
I think it's not really a good idea to ask for the user's role all the time, too many requests are made.
It would be better to ask once and save it on a variable in Razor. Then just check that variable whenever you need it.
By the way, if the roles are different, you don't even want to ask if the user is in that role rather than another one. Rather get the list of roles in a list and check if the role indicated is in the list.
Example (I'm not sure it will compile, look a the idea):
#using Microsoft.AspNetCore.Identity
#using Microsoft.AspNetCore.Mvc.Localization
#inject UserManager<ApplicationUser> UserManager
#{
ApplicationUser currentUser = await UserManager.GetUserAsync(User);
var roles = await UserManager.GetRolesAsync(currentUser);
bool isIntern = roles.Contains("intern");
bool isExtern = roles.Contains("extern");
bool isFoo = roles.Contains("foo");
...
}
then, further on
#if (isIntern)
{
<li>#Html.ActionLink("Log Time", "Index", "Time")</li>
}
else if (isExtern)
{
...
}
You can control the role of the user as many times as you want without having to make other requests and it's all much more readable.

access Auth() object from TryAuthenticate

I am logging into my service from a c# client like so:
serviceClient.Send<ServiceStack.ServiceInterface.Auth.AuthResponse>(
new ServiceStack.ServiceInterface.Auth.Auth() {
UserName = "xxx",
Password = "yyy" }
);
I would now like to use one of the unused strings in the Auth class to pass some additional information (like a programid). I am using my own subclassed CredentialsAuthProvider. Two questions:
Do you recommend any of the "extra" properties in the Auth class to stuff my programid over any others? I was considering using "State", will that mess anything up if I put a string in there?
Is there a way from within the TryAuthenticate override of my CredentialsAuthProvider to access the Auth class instance that was sent to me (so I can access the programid that I stuck into the State property).
Thank you.
Answered half of my question.
Not sure if any of the properties are better than any other, but I'm using "State" to stuff some extra data. Now that multiple end user programs are accessing the same service, sending a program ID in the State property lets me log the programs attempting to log into the service, along with the users.
If you are authenticating by overriding CredentialsAuthProvider, you can override the Authenticate method to gain access to the Authenticate object that is passed in from the user. From there, you can read the State property (or any other).

How to get a role using asp.net 4.0

I am new in ASP.NET 4.0 and C#..If I want hide/show menu item based on user logged in using web.sitemap, I must use a role and set it in web.config..I want to ask, where I can get that role?
if (User.IsInRole("rolename")) {
// what you wan't to do.
}
If you are really new to ASP.NET, you need to learn about Users and Roles. Try to use Membership API with standard elements like "Login". After that you have to write your own Users and Roles Provider with custom data structure. Then, use #Randolf R-F statement.
You need to use IPrincipal to store roles.
GenericIdentity userIdentity = new GenericIdentity((FormsIdentity)HttpContext.Current.User.Identity.Name);
string[] roles = { "rolename1", "rolename2", "rolename3" };
GenericPrincipal userPrincipal = new GenericPrincipal(userIdentity, roles);
Context.User = userPrincipal;
then you can check for user roles
if (User.IsInRole("rolename1")) {
// what you wan't to do.
}
If I understand correctly, you are saying that you have an asp.net web application project open and you want to know how to create a user and assign a role to them.
If you currently have web.config configured to use the default provider for rolemanager and membership provider then asp.net will take care of all the tricky stuff. The only things you have to do is go to your menu bar and select "Project -> ASP.NET Configuration". This will bring up a GUI for creating users and roles then assigning them. It should be a pretty self explanatory tool. That should meet your web.config use requirement too.
As suggested above from here if you have the user logged in you can do things like:
if (User.IsInRole("rolename"))
{ // what you wan't to do. }
Alternatively (and best for you atm) you should try using the logInView control in your toolbox.(I think that's what it's called) Do some googling on using these controls and it'll get you across the line.

How to force logout user when his/her username is changed by another user?

In my application I am using Forms-Authentication to sign in and sign out users.
One functionality is admin can change the username of other users. In that case, I need to sign out the user whose username is changed.
If I do not, due to their cookies set before, they gain access to application and receive error messages (since their username does not exist and there are parts where I use their username for some functionality).
How can I force these users to log out using Forms-Authentication ?
UPDATE :
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string controller = filterContext.RouteData.Values["controller"].ToString();
string action = filterContext.RouteData.Values["action"].ToString(); ;
// Below returns the previous username, which does not exist anymore in db.
string userName = HttpContext.Current.User.Identity.Name;
UnitOfWork unitOfWork = new UnitOfWork();
if (!unitOfWork.UserRepository.UserExists(userName))
{
FormsAuthentication.SignOut();
filterContext.HttpContext.Session.Clear();
filterContext.HttpContext.Session.Abandon();
// I am not using Roles.
}
unitOfWork.Dispose();
base.OnActionExecuting(filterContext);
}
In my customer global filter, I check whether user exist or not, if not I sign them out. However, it is not working. By working I mean they pass the authentication and gain access to application.
Thanks in advance.
Here's what you do to force user to sign out:
public void UserPasswordChangedHandler()
{
FormsAuthentication.SignOut();
Roles.DeleteCookie();
Session.Clear();
}
I don't think line by line explanation required, its self explanatory enough.
Please let me know if I am mistaken.
Update
Straightforward answer to your additional question is to keep per user boolean tracking if his data was updated by admin and if yes - just redirect him to login page.
Please see following articles for forced logout using forms authentication information:
How can I force a user to log out
How can I force a log out of all users for a website,
ASP.NET forms authentication forced logout
How to log off multiple MembershipUsers that are not the current user
Update 2
Clearing cookies
HowTo: create and remove Cookies with ASP.NET MVC
How do you clear cookies in ASP NET MVC 3 and C#
How do I invalidate a bad authentication cookie
Hope this help you.
When a user needs to become invalidated you must add their details to some kind of internal static list.
Then on every page request (possibly using Application_BeginRequest) see if that current user is in that list, and if so to call FormsAuthentication.SignOut there-and-then.
It seems like a bit of a hack, but it's the best I can think of right now.
Note that removing a user-in-absentia's session state is another issue entirely.

Categories

Resources