I am new to MVC and actually new to web development all together. I have about 7 years of development experience but in services, database, object models, etc.. basically middle-tier and back-end development. I am trying to learn ASP.NET and decided to build a site using MVC3 for a personal site for myself. I will be hosting this from an account at dotnet-hosts.com. Here is my question... I don't have a domain and I will be using the built in membership provider. I noticed in the auto generated code that was created when I added the project template that in the AccountController in the method ChangePassword (ChangePasswordModel model) there is this line of code...
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
My question is specifically around User.Identity.Name, this looks like it would be returning the Windows user name just like Environment.UserName would. The Visual Studio template I used is the (Mobile Ready HTML5 MVC.NET) as I want to be able to support clients from any device...Windows PC, Apple, Windows Phone, iPhone, etc... If the call to User.Identity.Name is correct then I would like to ask how does this work on devices that are not Windows like an iPhone? If my assumption is correct that this will only work for Windows computers with a domain then how can I achieve this? would I need to perhaps use some caching? If so could I maybe grab the user name and their IP address to be used as the cache key from the Authentication page?
My high level question is... How do I get the current logged in user's userName regardless of the device/platform? I know this question is probably not written well and may be hard to understand... I apologize for that. I am new to web development and trying to get my feet wet and would like to start to the latest technology.
The call is correct. The User.Identity.Name is filled out by whatever authentication provider is in use - Windows authentication, Forms authentication, some custom authentication provider, or whatever. It isn't bound to a specific user "type". The authentication provider has the responsibility of making sure the Identity object corresponds to the current user on every request. Usually that part is taken care of using a combination of cookies and database.
The MVC template (although I haven't had a look at the template since MVC 2) uses ASP.NET's Membership class, which in turn uses a membership provider - for example SqlMembershipProvider or ActiveDirectoryMembershipProvider - the former stores your users' credentials (username and password etc.) in an SQL Server database, the latter uses Active Directory (i.e. primarily Windows logons). SqlMembershipProvider is the default, and MVC is set up to use a local SQLExpress database file as its user store.
The authentication provider that's implemented in the template project uses FormsAuthentication, which does the login procedure through a simple HTML form (the one in the LogOn view) and keeps the user signed in by way of an encrypted cookie. Works on any platform.
The setup for both FormsAuthentication and SqlMembershipProvider can be found in web.config (the one in the root of the site). There you can find the connection strings for the SQLExpress database (and e.g. change them to use a "real" SQL Server if needed), the timeout for logins etc.
(Note that you can do a lot of that configuration easily in a GUI through the "ASP.NET Configuration" button in the toolbar of Solution Explorer in Visual Studio - it also provides an easy way to set up the first users).
In short, it's all ready to go - and doesn't lock out non-Windows users.
Like you said User.Identity.Name is indeed correct. for returning the logged in users name. But the membership section like you said, provides only windows accounts. You can use similar without the user of windows accounts, to work in every scenario, and can still verify against windows if present. If you call it without membership, and follow the default MVC3 template it should work fine.
String Username = User.Identity.Name;
When you log on, using the template MVC3, it creates an authcookie. See account controller code. Here, two parameters are passed into it. The username, and to persist (when browser is closed - login is still cached).
The username is a string field, which is what is called by User.Identity.Name and infact, anything can be put into it, and is not in anyway linked to Windows login.
You could test the login via method you desire, and if yes, set a cookie using the authcookie method. (its encripted). And set the username to what ever you want. And if your verification of the user fails, dont create one, and redrect back to page.
See the example code. This is all from memory, as I dont have code infront of me for reference. But its all in the account controller, Login Action.
When the cookie is set, The users login state is cached for the session. You will need to ensure the user is logged in when visiting a webpage. Otherwise loggin in will be pointless. This is a simple attribute on the controller/action.
Note: dont do this to the Account/logon controller, as you wont be able to visit the logon page, as you are not logged in.
[Authorize]
public ActionResult DoSomething()
{
// ...
}
Hope I have helped.
Related
I have been trying to get my head around OWIN and Identity to get it play well with my Existing app. The application was coded in Plain ASP.Net and I want to port it to MVC to organize it better. The app have established database including Users table so using Identity table is not option. The wall I hit with Identity is that it requires Email and my system is NOT supposed to demand user email. After trying almost everything (days reading almost every answer on the subject) I have come to conclusion that it will take more time to get it done, and that brings me to my question.
Being PHP guy (jumped to ASP.Net not long ago) I could easily make a working login in 10minutes and now I need a thing like it, something like
$user = User::findByUserName($username); //somehow here I find user from database
if($user!=null)
{
$_SESSION['isLoggedIn'] = true;
$_SESSION['fullname'] = $user->fullName;
}
else
{
//redirect to login with error
}
I cannot find a session class in MVC5. Is that even possible? If no, is there a simple route to solve my issue?
The most common way to cache is to either place it in the 'Global' server cache or place it in the session. There are other ways to save state and I would recommend looking into alternatives as the techniques below use the System.Web namespace. System.Web will not be included in MVC6.
Global Cache
//Save User
System.Web.HttpContext.Current.Cache.Insert(
"CurrentUser",
User,
null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
new TimeSpan(0,20,0),
System.Web.Caching.CacheItemPriority.Normal,
null);
//Get User
User user=(User)System.Web.HttpContext.Current.Cache["CurrentUser"];
Session Cache
//Save User
HttpContext.Current.Session.Add("CurrentUser",User);
//Get User
User=(User)HttpContext.Current.Session["CurrentUser"];
you could basically tell owin manuelly to authenticate the user without using the identity-stuff (i dont like it either, at least for small projects). Then you could easily use the default authorize anotation
Heres a tutorial: OWIN AUTH
I have a weird problem with external google authentication. Basically I'm using almost the same code which is generated by default MVC4 internet application.
Everything works fine on my local machine. I can login with google account.
But when I change the connectrion string to external database on the web, this code:
OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false)
returns FALSE so it's not logging in, and I don't know any reason why.
The same happens when I publish whole website on the web (with the same extenral connection string which seems to generate the problem on my local machine).
The ONLY difference between working and not working version is different connection string in web.config.
Did anyone suffer the same problem?
EDIT
I didn't mention. The other connection string works correctly. It gets all the data from database, but somehow google authentication silently fails. It just does not log in. Database schema and the data is identical to my local database (I have even generated sql script with data and applied in on external database)
Also the user which I want to log in is in this external database.
So what is happening:
OAuthWebSecurity.Login returns false so I automatically want to register new user, but then the user is found already in database and I'm getting something like "the user already exists in database".
Very weird.
Did you create the user in external DB with CreateOrUpdateAccount method? Probably you do not have associated any user with the Google credentials which you are using...
The login process works in following steps:
Hey Google, tell me who is logged in.
Google tells you some UserId X
OAuthWebSecurity asks your membership provider for user with GoogleUserId X
If there exists such a user, it logs him in and if not, login fails...
I am developing an application in asp.net using vs2010.
In application, Admin can create different user accounts using Microsoft member registration wizard.
Users can Login with created credential using Microsoft login control.
Now,I have to access this Logedin user's UserID and UserName in entire application's different forms.
Currently I am accessing this details by writing code in all forms by
MembershipUser newUser = Membership.GetUser();
Guid newUserId = (Guid)newUser.ProviderUserKey;
So, where can i store this login user's UserID and UserName in a common place. So I can access this details from common place?
Please Help me.
Thanks,
Well - that depends fully on where you persist the data for your application.
If you use a database for storage, then logically the data should belong in there, in a user table, with a connectionstring to the database in your application's configuration.
If not using databases, then you properly need some file based storage, for example XML or something you invent yourself and then have a parser which serialize/deserialize the data from files.
In both instances, you'll need to consider security and hashing/salting and make sure the data is kept secure.
I tend to use a static helper class, which stores (and loads) data in HttpContext.Items for the duration of any request. So you would just need to call GetUser once per request. If even that is too much for you, you can use a Session, for example - but don't forget that sessions only live for so long, so be prepared to reload the data if it's lost due to session timeout.
The static class has to be somewhere accessible from the whole application - in a web site project, this means the App_Code folder.
In my ASP.NET web application I need to save a password in a database. (Note that this is not a login password and a user providing such password is given a visible security warning.) I was thinking to provide some scrambling for the password before placing it into the database.
So I was thinking to do this:
byte[] bytesToSave_Scrambled =
System.Security.Cryptography.ProtectedData.Protect(
System.Text.Encoding.UTF8.GetBytes(password), null,
System.Security.Cryptography.DataProtectionScope.CurrentUser);
The issue is that according to this page, when I use DataProtectionScope.CurrentUser only that same user account will be able to decode it back. I can of course use DataProtectionScope.LocalMachine instead, but the documentation says that it is less secure.
So my question is, when my web app runs on a web server, does it run under the same Windows user account to ensure consistency of the scrambling method above?
My asp.net mvc site needs some kind of authorization but it is rather different than the usual concept of users and thus membership.
It will be used more for preferences then for authentication actually. Accounts without any password should be possible (and will initially be the only type) and an account can also be used by multiple users at once. Depending on the user group it could be for example that all users of a certain region get a shared account.
This is a decision from the client('s marketing division) and is not up for discussion.
A certain landing page takes (only) a userId in the url that will load up an account which in turn has some preferences linked to it that can be used throughout the rest of the site.
If a user doesn't start at the landing page or the sent accountId doesn't match a record in the system, he/she will be assigned the default account that has default preferences.
I was thinking of not re-inventing the wheel (somebody should find a new expression for this) and use the asp.net Membership system.
But the whole system is based around required passwords, email and single sessions per user, which are all things I can't provide.
Because the situation is a bit unconventional I thought a custom MembershipProvider etc would be in place. But it seems the gist of this is inheriting from the regular Membership classes. The methods of these classes all require things I am not needing.
Any suggestions
You could use the standard Membership provider and using the Built in .Validate() method sending the Username and a Password that is "standard" for all accounts without authentication.
Have 2 different User Controls 1 for "Validated Login with Password" and one for "Share Account without password", each uses Membership-login but the latter needs to have a bit set on the field of the member that says "Public Account = True / 1 "
Good luck, seems like a fun project, would be cool to see the outcome ;)
By the way, you don't need to share the session, or you could, just stored the session in the database and map the session to a user instead of a cookie, might work?
As requested i'll elaborate on different user controls. Briefly i would have 2 Controls, one maybe called GlobalLogin and one called UserLogin, where GlobalLogin displays a Form which only has the Username, when submitted this will trigger a function that uses, as i stated before, a function which calls the Validate method in the Membership provider, with a pre-set password.
As a reflection, see all "Not logged in with password"-users as anonymous and treat them the same way, the only thing that is different is that they can access user-specific areas. This control also needs to check that a certain field in the database is set, such as a "Allows Globally Used Account Without Password"-field, where in this case, the bit / boolean needs to be true for this login to be accepted.
Now to the other part, the Control which handles Password Protected Accounts, this requires both Username & Password and this calls the Validate with these settings. Now, remember that when logged in with password, you can change your password, this SHOULD NOT be possible with a Global Account, because then your global password wouldnt work :)
There is detailed information on the Membership Provider at http://msdn.microsoft.com/en-us/library/f1kyba5e.aspx. Basically you need to create new provider, or derive from the existing, and overload the ValidateUser method to always return true.