81

I'm developing an ASP.NET MVC 5 application. I have an existing DB, from which I created my ADO.NET Entity Data Model. I have a table in that DB which contains "username" and "password" column, and I want to use them to implement authentication and authorization in my Webapp; I cannot create any other database or table or column and I cannot use the standard Identity authentication, because of customer's requirements. I don't need to manage signup, password changing or other stuffs: just login with password and username. How can I do that?

1 Answers1

161

Yes, you can. Authentication and Authorization parts work independently. If you have your own authentication service you can just use OWIN's authorization part. Consider you already have a UserManager which validates username and password. Therefore you can write the following code in your post back login action:

[HttpPost]
public ActionResult Login(string username, string password)
{
    if (new UserManager().IsValid(username, password))
    {
        var ident = new ClaimsIdentity(
          new[] { 
              // adding following 2 claim just for supporting default antiforgery provider
              new Claim(ClaimTypes.NameIdentifier, username),
              new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),

              new Claim(ClaimTypes.Name,username),

              // optionally you could add roles if any
              new Claim(ClaimTypes.Role, "RoleName"),
              new Claim(ClaimTypes.Role, "AnotherRole"),

          },
          DefaultAuthenticationTypes.ApplicationCookie);

        HttpContext.GetOwinContext().Authentication.SignIn(
           new AuthenticationProperties { IsPersistent = false }, ident);
        return RedirectToAction("MyAction"); // auth succeed 
    }
    // invalid username or password
    ModelState.AddModelError("", "invalid username or password");
    return View();
}

And your user manager can be something like this:

class UserManager
{
    public bool IsValid(string username, string password)
    {
         using(var db=new MyDbContext()) // use your DbConext
         {
             // for the sake of simplicity I use plain text passwords
             // in real world hashing and salting techniques must be implemented   
             return db.Users.Any(u=>u.Username==username 
                 && u.Password==password); 
         }
    }
}

In the end, you can protect your actions or controllers by adding an Authorize attribute.

[Authorize]
public ActionResult MySecretAction()
{
    // all authorized users can use this method
    // we have accessed current user principal by calling also
    // HttpContext.User
}

[Authorize(Roles="Admin")]
public ActionResult MySecretAction()
{
    // just Admin users have access to this method
} 
Sam FarajpourGhamari
  • 14,601
  • 4
  • 52
  • 56
  • I'm sorry, I'm a newbie with authentication. Where shuold I use this code? How can I tell MVC to take username e password fields from my EF class? – Giacomo Santarnecchi Jul 23 '15 at 13:52
  • 7
    I have just updated my post to answer your questions. – Sam FarajpourGhamari Jul 23 '15 at 14:18
  • Is there a way to use [Authorize] Annotations with your example? – AME Jan 14 '16 at 10:14
  • @PiNg2Eiw Yes. since I used Identity's standard pipeline so you could use `Authorize` attribute as well. Simply add `[Authorize]` attribute to the action or controller that you want restrict it. [See other similar answer which I have already answered](http://stackoverflow.com/questions/32080212/asp-net-identity-2-0-custom-login-method/32170577#32170577). – Sam FarajpourGhamari Jan 14 '16 at 11:12
  • 5
    Hey, I wanted to let you know that your github example ( for tokenauth ) solved my problems, thanks a lot ! I'd upvote your example 1000 times if I could :) – AME Jan 15 '16 at 12:02
  • 6
    nuget packages needed: - Microsoft.AspNet.Identity.Core - Microsoft.AspNet.Identity.Owin - Microsoft.Owin - Microsoft.Owin.Host.SystemWeb - Microsoft.Owin.Security - Microsoft.Owin.Security.Cookies - Microsoft.Owin.Security.OAuth - Owin – Matthieu May 13 '16 at 14:39
  • 5
    I wish you had open bounty for this question so we can give you +1000. Please post this somewhere on blog so search engines can reach this. It is so easy and elegant solution. I spent two days reading about OWIN and OAuth2 provides and I was unable do connect wires. – adopilot May 26 '16 at 10:07
  • 2
    @SamFarajpourGhamari: Can you explain why that long const string is required in the `Login` code? `... new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string")` I checked that the code works just fine without it! – S.Serpooshan Oct 29 '16 at 11:53
  • @saeedserpooshan those 2 claims are used to generate and validate anti-forgery tokens. I have covered this issue in [this question](http://stackoverflow.com/questions/32110852/mvc-anti-forgery-token-error-on-scaffold) before. – Sam FarajpourGhamari Oct 29 '16 at 16:10
  • @SamFarajpourGhamari Ok, i see. but i think using `AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;` in startup.cs is better choise as then we only need to add NameIdentifier (userId) to claims during login and that identity provider string is not required. It is not nice to use such a long constant string by hard coding! – S.Serpooshan Oct 30 '16 at 10:23
  • 1
    This solution seems to be OWIN specific. -- How do we do it in regular MVC? -- At any rate, just adding the NuGet packages is not enough, you'll get a bunch of code about missing `OwinStartupAttribute`s, and missing configuration settings... – BrainSlugs83 Apr 06 '17 at 21:08
  • @BrainSlugs83 In this answer I assumed Identity and OWIN already present in the project but questioner wants to customize it. Installing and implementing Identity and OWIN is another story which I [answered in other question](http://stackoverflow.com/questions/31960433/adding-asp-net-mvc5-identity-authentication-to-an-existing-project/31963828#31963828). – Sam FarajpourGhamari Apr 06 '17 at 21:21
  • 1
    Well, I guess that the answer is trying to answer about the OWin specific part, but for anyone reading this, never ever implement UserManager class like this: passwords must be hashed and never stored as plain readable text in database – Oscar Jul 11 '17 at 19:14
  • 1
    @Oscar indeed passwords must be hashed in the real world. I updated the post and pointed out this to resolve ambiguity. Thank you for mentioning it. – Sam FarajpourGhamari Jul 11 '17 at 19:56
  • @SamFarajpourGhamari thanks for your post! It´s really useful! Do you think that this method plus adding the [ValidateAntiForgeryToken] attribute to the Login method and hashing and salting passwords would be secure enough to deploy it to production? Thank you in advanced! – Ignacio Oct 15 '18 at 13:25
  • @Ignacio of course you have to use ValidateAntiForgeryToken also Identity framework already have a awesome hashing and salting mechanism out of the box. – Sam FarajpourGhamari Oct 16 '18 at 16:40
  • @SamFarajpourGhamari Please kindly accept one more question: we won´t need the Identity default database schema in any way, right? I mean, we are going to manage our own user validation and there is no need to have the Identity tables in a SQL Server or LocalDB (as it works in Visual Studio while developing). – Ignacio May 22 '20 at 22:38
  • @lgnacio Yes you don't need Identity's default schema as I did in this example. I simply bypassed Identity's UserStore and UserManager classes but used other parts to authenticate the user. Also keep in mind you could implement your own UserStore with IUserStore interface and integrate with Identity's UserManager as well. – Sam FarajpourGhamari May 22 '20 at 22:57
  • @SamFarajpourGhamari Thank you very much for your fast response! Yes we are not using the Identity persistance. The problem is that when the authentication cookie expires we get an error because the SQL Server is not responding. It seems like it´s trying to validate something against the Identity database. What we have done is catch the exception in the Application_Error (inside Global.asax file) and redirect to login but the problem is that the website waits for the timeout to connect to the SQL Server before generating the error. This will only happend when the cookie has expired. – Ignacio May 22 '20 at 23:50
  • @lgnacio it seems your `regenerateIdentity` on the `CookieAuthenticationProvider` still uses default `UserStore`. – Sam FarajpourGhamari May 27 '20 at 19:09
  • This approach is fine, but take in consideration that it wont update the claims until the user log out. The solution to this is a bit more expensive in order you get check the roles to the database everytime. – Leandro Bardelli Oct 06 '20 at 15:31