191

I have seen lots of similar pages on the web, but most of them use a new project instead of an existing one, or don't have the necessary features. So, I have an existing MVC 5 project and want to integrate ASP.NET MVC5 Identity with log in, email confirmation and password reset features.

In addition, I also need to create all the necessary tables on the database i.e. Users, Roles, groups, etc. (I have used EF Code-First in my project). Is there an article or sample that corresponds to these needs?

Farimah
  • 27
  • 9
Jack
  • 1
  • 21
  • 118
  • 236
  • 1
    What a great queston and what a simple solutin given just below. I loved it to read through and badly needed to integrate in my existing project too. – Ishwor Khanal Oct 30 '18 at 11:35

4 Answers4

314

Configuring Identity to your existing project is not hard thing. You must install some NuGet package and do some small configuration.

First install these NuGet packages with Package Manager Console:

PM> Install-Package Microsoft.AspNet.Identity.Owin 
PM> Install-Package Microsoft.AspNet.Identity.EntityFramework
PM> Install-Package Microsoft.Owin.Host.SystemWeb 

Add a user class and with IdentityUser inheritance:

public class AppUser : IdentityUser
{
    //add your custom properties which have not included in IdentityUser before
    public string MyExtraProperty { get; set; }  
}

Do same thing for role:

public class AppRole : IdentityRole
{
    public AppRole() : base() { }
    public AppRole(string name) : base(name) { }
    // extra properties here 
}

Change your DbContext parent from DbContext to IdentityDbContext<AppUser> like this:

public class MyDbContext : IdentityDbContext<AppUser>
{
    // Other part of codes still same 
    // You don't need to add AppUser and AppRole 
    // since automatically added by inheriting form IdentityDbContext<AppUser>
}

If you use the same connection string and enabled migration, EF will create necessary tables for you.

Optionally, you could extend UserManager to add your desired configuration and customization:

public class AppUserManager : UserManager<AppUser>
{
    public AppUserManager(IUserStore<AppUser> store)
        : base(store)
    {
    }

    // this method is called by Owin therefore this is the best place to configure your User Manager
    public static AppUserManager Create(
        IdentityFactoryOptions<AppUserManager> options, IOwinContext context)
    {
        var manager = new AppUserManager(
            new UserStore<AppUser>(context.Get<MyDbContext>()));

        // optionally configure your manager
        // ...

        return manager;
    }
}

Since Identity is based on OWIN you need to configure OWIN too:

Add a class to App_Start folder (or anywhere else if you want). This class is used by OWIN. This will be your startup class.

namespace MyAppNamespace
{
    public class IdentityConfig
    {
        public void Configuration(IAppBuilder app)
        {
            app.CreatePerOwinContext(() => new MyDbContext());
            app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
            app.CreatePerOwinContext<RoleManager<AppRole>>((options, context) =>
                new RoleManager<AppRole>(
                    new RoleStore<AppRole>(context.Get<MyDbContext>())));

            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Home/Login"),
            });
        }
    }
}

Almost done just add this line of code to your web.config file so OWIN could find your startup class.

<appSettings>
    <!-- other setting here -->
    <add key="owin:AppStartup" value="MyAppNamespace.IdentityConfig" />
</appSettings>

Now in entire project you could use Identity just like any new project had already installed by VS. Consider login action for example

[HttpPost]
public ActionResult Login(LoginViewModel login)
{
    if (ModelState.IsValid)
    {
        var userManager = HttpContext.GetOwinContext().GetUserManager<AppUserManager>();
        var authManager = HttpContext.GetOwinContext().Authentication;

        AppUser user = userManager.Find(login.UserName, login.Password);
        if (user != null)
        {
            var ident = userManager.CreateIdentity(user, 
                DefaultAuthenticationTypes.ApplicationCookie);
            //use the instance that has been created. 
            authManager.SignIn(
                new AuthenticationProperties { IsPersistent = false }, ident);
            return Redirect(login.ReturnUrl ?? Url.Action("Index", "Home"));
        }
    }
    ModelState.AddModelError("", "Invalid username or password");
    return View(login);
}

You could make roles and add to your users:

public ActionResult CreateRole(string roleName)
{
    var roleManager=HttpContext.GetOwinContext().GetUserManager<RoleManager<AppRole>>();

    if (!roleManager.RoleExists(roleName))
        roleManager.Create(new AppRole(roleName));
    // rest of code
} 

You could also add a role to a user, like this:

UserManager.AddToRole(UserManager.FindByName("username").Id, "roleName");

By using Authorize you could guard your actions or controllers:

[Authorize]
public ActionResult MySecretAction() {}

or

[Authorize(Roles = "Admin")]]
public ActionResult MySecretAction() {}

You can also install additional packages and configure them to meet your requirement like Microsoft.Owin.Security.Facebook or whichever you want.

Note: Don't forget to add relevant namespaces to your files:

using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;

You could also see my other answers like this and this for advanced use of Identity.

Pradeep Singh
  • 432
  • 5
  • 11
Sam FarajpourGhamari
  • 14,601
  • 4
  • 52
  • 56
  • Many thanks for your answer. Before starting to apply these solution, could you please clarify me which tables (or entities) should I have to create for authentication i.e. User, UserGroup, UserRole? On the other hand, I found an article on [ASP.NET Identity 2.0: Implementing Group-Based Permissions Management](http://www.codeproject.com/Articles/808903/ASP-NET-Identity-Implementing-Group-Based-Permissi). >>> – Jack Aug 12 '15 at 15:05
  • >>> As I have no enough experience, your solution seems to be better for me. However, I just want to know if that solution would be much more useful for the future? And what is the difference between these two solutions? – Jack Aug 12 '15 at 15:05
  • 2
    Both solutions look similar. I have used `AppRole` and Identity's role manager to classify user. And since `Role`s and `RoleManager` have already implemented by Identity itself you don't need rewrite already implemented code. I will update the post to show you how you can use roles. And as I said before you just need add `AppUser` and `AppRole` entities to initialize Identity. By inheriting your `DbContext` from `IdentityDbContext` all necessary tables add your table. You don't need do anything just enable migration. – Sam FarajpourGhamari Aug 12 '15 at 15:26
  • Thanks, I was just started to apply and inform you step by step after encountering a problem. When did you do last update? On the other hand, I have three projects in my solution: **WebUI**, **UnitTest** and **Domain**. As all the Entities and DbContext definitions are located on **Domain** project, should I install all the nuget packages in it? – Jack Aug 12 '15 at 15:51
  • 2
    I just added some sample usage. Install `Microsoft.AspNet.Identity.EntityFramework` to your Domain and other for UI. – Sam FarajpourGhamari Aug 12 '15 at 15:56
  • Your explanations are really perfect and I can proceed easily. Some points that I need to be clarified : **1)** After installing some packages, my old `web.config` file was completely changed and now there are two separate files under it : `Web.Debug.config` and `Web.Release.config`. I have a backup of the previous `web.config` file, but I do not know if I should replace the new ones with the old one. **2)** I added **AppUser, AppRole and AppUserManager** to the Domain project as the other entity classes that I created before. Is there any problem regarding to placement? – Jack Aug 12 '15 at 17:49
  • **3)** I installed `Microsoft.AspNet.Identity.EntityFramework` to my `Domain` and other for `WebUI` project as you said. But, as I encounter some errors, I had to install the `Microsoft.AspNet.Identity.EntityFramework` to my `WebUI` as well. Is it OK, or might there be a problem? (The origin of the error is creating an instance of my `DbContext` in `WebUI` project). **4)** I think there will be two tables created in the database as `AppUser` and `AppRole`. If other, could you please explain a little bit (about the relations and the usage). – Jack Aug 12 '15 at 17:52
  • 2
    1) Don't worry about your `web.config`. Don't replace old one. [Read this for more info](https://msdn.microsoft.com/en-us/library/dd465326(v=vs.110).aspx). I think your MVC also upgraded. – Sam FarajpourGhamari Aug 12 '15 at 17:59
  • 1
    2) You did right. 3) no problem. You will have 5 new table `AspNetRoles` `AspNetUserClaims` `AspNetUserLogins` `AspNetUserRoles` and `AspNetUsers` – Sam FarajpourGhamari Aug 12 '15 at 17:59
  • 1) But there is almost nothing in the new web.config files and none of my settings, connection strings, etc. were transformed to this new file. It does not seem to be normal **:(** Shall I copy the content of the old file to the new file? If so, to all of the new 2 file (there was only one file before, but now two files)? – Jack Aug 12 '15 at 18:07
  • 1
    By saying new `web.config` files you mean `Web.Debug.config` and `Web.Relase.config`? If yes don't worry about them for now. Is your configuration still exist in `web.config`? – Sam FarajpourGhamari Aug 12 '15 at 18:12
  • After installing nugets, there is nothing in `web.config` and by clicking `web.config`, `Web.Debug.config` and `Web.Relase.config` files are collapsed. Now I copied the old `web.config` and then updated the `Web.Debug.config` and `Web.Relase.config` under it. After debugging project I encounter an error `"Could not load file or assembly 'Microsoft.Owin, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)"` but forget it. I will solve – Jack Aug 13 '15 at 06:36
  • What about the question 3 and 4 ? – Jack Aug 13 '15 at 06:36
  • 1
    You must copy your custom configuration inside `web.config` file. `web.Relase.config` and `Web.Debug.config` just are transform not actual config. I answered 3 and 4 also see my earlier comments – Sam FarajpourGhamari Aug 13 '15 at 06:52
  • Many thanks again for your kind help. Although I have encountered several problems or errors, I will try it and I hope this is one of the best approach. Voted++ – Jack Aug 18 '15 at 12:43
  • I just followed these steps to the T and it works except the login does not hold. It asks again every 3-6 page clicks. Any idea? – blubberbo Jul 13 '16 at 00:38
  • 4
    I just read through all the comments you left helping Clint Eastwood, Nice Job!! The world needs more people like You plusOne – Chef_Code Dec 12 '16 at 23:45
  • @SamFarajpourGhamari Your answer looks pretty good and I just started implementing it, anyway I can't find where to put the classes `AppUser`, `AppUserManager`, .. Do they belong to somewhere "special" like Model or does it not matter where I put them? – Stefan Jan 31 '17 at 11:27
  • @Stefan `AppUser` is a model object put it beside other model objects like `Book` or `Car`. `AppUserManager` is a service put it beside other services as well. – Sam FarajpourGhamari Jan 31 '17 at 16:14
  • @SamFarajpourGhamari thanks, that's what I did now. But my problem is, in my current test app I don't have any other models and (I don't know why) my localdb is not created (sorry, I'm very new to MVC). How to fix this? – Stefan Feb 01 '17 at 17:50
  • @SamFarajpourGhamari Never mind, I got it working by explicitly passing the name of the connectionstring to the base constructor of my DbContext, although it was defined with the full qualified name in the web.config. Thank you anyway :) – Stefan Feb 02 '17 at 22:19
  • I think that this line is not required am I correct= – Emil Dec 18 '17 at 12:18
  • Does inheriting from an IdentityDbContext change anything else for other users/applications that might access the database outside of the MVC/OWIN authentication path? – JordanTDN Apr 05 '18 at 11:42
  • @SamFarajpourGhamari is this the same for net core 2.1? – Gianlucca Dec 01 '18 at 22:32
  • 1
    Hi @SamFarajpourGhamari I did exactly as you did but I get an error at new RoleStore(context.Get()))); saying NO OVERLOAD FOR GET TAKES 0 ARGUMENTS – azure boy Jan 26 '19 at 08:51
  • 1
    @azureboy Are you sure you installed and added relevant NuGet packages and namespaces? the get method without any argument is an extension method which is available only if you installed and added relevant package and namespace – Sam FarajpourGhamari Jan 27 '19 at 16:30
  • @SamFarajpourGhamari: yeah, still it doesn;t and believe me that's a killer level frustration. – azure boy Jan 28 '19 at 08:31
  • 2
    It almost worked. But I was getting error during migration `EntityType 'IdentityUserRole' has no key defined` and I solved it by adding `base.OnModelCreating(modelBuilder);` to the `OnModelCreating` function. – Wahid Masud Mar 29 '19 at 10:31
  • 1
    Thank you so much for this code, it worked like a charm and it was a lot easier to implement and understand than some of the other code I've seen in this platform. – caliche2000 Mar 04 '20 at 15:19
29

This is what I did to integrate Identity with an existing database.

  1. Create a sample MVC project with MVC template. This has all the code needed for Identity implementation - Startup.Auth.cs, IdentityConfig.cs, Account Controller code, Manage Controller, Models and related views.

  2. Install the necessary nuget packages for Identity and OWIN. You will get an idea by seeing the references in the sample Project and the answer by @Sam

  3. Copy all these code to your existing project. Please note don't forget to add the "DefaultConnection" connection string for Identity to map to your database. Please check the ApplicationDBContext class in IdentityModel.cs where you will find the reference to "DefaultConnection" connection string.

  4. This is the SQL script I ran on my existing database to create necessary tables:

    USE ["YourDatabse"]
    GO
    /****** Object:  Table [dbo].[AspNetRoles]    Script Date: 16-Aug-15 6:52:25 PM ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TABLE [dbo].[AspNetRoles](
    [Id] [nvarchar](128) NOT NULL,
    [Name] [nvarchar](256) NOT NULL,
    CONSTRAINT [PK_dbo.AspNetRoles] PRIMARY KEY CLUSTERED 
    (
      [Id] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    /****** Object:  Table [dbo].[AspNetUserClaims]    Script Date: 16-Aug-15 6:52:25 PM ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TABLE [dbo].[AspNetUserClaims](
       [Id] [int] IDENTITY(1,1) NOT NULL,
       [UserId] [nvarchar](128) NOT NULL,
       [ClaimType] [nvarchar](max) NULL,
       [ClaimValue] [nvarchar](max) NULL,
    CONSTRAINT [PK_dbo.AspNetUserClaims] PRIMARY KEY CLUSTERED 
    (
       [Id] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    
    GO
    /****** Object:  Table [dbo].[AspNetUserLogins]    Script Date: 16-Aug-15 6:52:25 PM ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TABLE [dbo].[AspNetUserLogins](
        [LoginProvider] [nvarchar](128) NOT NULL,
        [ProviderKey] [nvarchar](128) NOT NULL,
        [UserId] [nvarchar](128) NOT NULL,
    CONSTRAINT [PK_dbo.AspNetUserLogins] PRIMARY KEY CLUSTERED 
    (
        [LoginProvider] ASC,
        [ProviderKey] ASC,
        [UserId] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    /****** Object:  Table [dbo].[AspNetUserRoles]    Script Date: 16-Aug-15 6:52:25 PM ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TABLE [dbo].[AspNetUserRoles](
       [UserId] [nvarchar](128) NOT NULL,
       [RoleId] [nvarchar](128) NOT NULL,
    CONSTRAINT [PK_dbo.AspNetUserRoles] PRIMARY KEY CLUSTERED 
    (
        [UserId] ASC,
        [RoleId] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    /****** Object:  Table [dbo].[AspNetUsers]    Script Date: 16-Aug-15 6:52:25 PM ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TABLE [dbo].[AspNetUsers](
        [Id] [nvarchar](128) NOT NULL,
        [Email] [nvarchar](256) NULL,
        [EmailConfirmed] [bit] NOT NULL,
        [PasswordHash] [nvarchar](max) NULL,
        [SecurityStamp] [nvarchar](max) NULL,
        [PhoneNumber] [nvarchar](max) NULL,
        [PhoneNumberConfirmed] [bit] NOT NULL,
        [TwoFactorEnabled] [bit] NOT NULL,
        [LockoutEndDateUtc] [datetime] NULL,
        [LockoutEnabled] [bit] NOT NULL,
        [AccessFailedCount] [int] NOT NULL,
        [UserName] [nvarchar](256) NOT NULL,
    CONSTRAINT [PK_dbo.AspNetUsers] PRIMARY KEY CLUSTERED 
    (
        [Id] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    
     GO
     ALTER TABLE [dbo].[AspNetUserClaims]  WITH CHECK ADD  CONSTRAINT [FK_dbo.AspNetUserClaims_dbo.AspNetUsers_UserId] FOREIGN KEY([UserId])
     REFERENCES [dbo].[AspNetUsers] ([Id])
     ON DELETE CASCADE
     GO
     ALTER TABLE [dbo].[AspNetUserClaims] CHECK CONSTRAINT [FK_dbo.AspNetUserClaims_dbo.AspNetUsers_UserId]
     GO
     ALTER TABLE [dbo].[AspNetUserLogins]  WITH CHECK ADD  CONSTRAINT [FK_dbo.AspNetUserLogins_dbo.AspNetUsers_UserId] FOREIGN KEY([UserId])
     REFERENCES [dbo].[AspNetUsers] ([Id])
     ON DELETE CASCADE
     GO
     ALTER TABLE [dbo].[AspNetUserLogins] CHECK CONSTRAINT [FK_dbo.AspNetUserLogins_dbo.AspNetUsers_UserId]
     GO
     ALTER TABLE [dbo].[AspNetUserRoles]  WITH CHECK ADD  CONSTRAINT [FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId] FOREIGN KEY([RoleId])
     REFERENCES [dbo].[AspNetRoles] ([Id])
     ON DELETE CASCADE
     GO
     ALTER TABLE [dbo].[AspNetUserRoles] CHECK CONSTRAINT [FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId]
     GO
     ALTER TABLE [dbo].[AspNetUserRoles]  WITH CHECK ADD  CONSTRAINT [FK_dbo.AspNetUserRoles_dbo.AspNetUsers_UserId] FOREIGN KEY([UserId])
     REFERENCES [dbo].[AspNetUsers] ([Id])
     ON DELETE CASCADE
     GO
     ALTER TABLE [dbo].[AspNetUserRoles] CHECK CONSTRAINT [FK_dbo.AspNetUserRoles_dbo.AspNetUsers_UserId]
     GO
    
  5. Check and solve any remaining errors and you are done. Identity will handle the rest :)

Shyamal Parikh
  • 2,988
  • 4
  • 37
  • 78
  • 2
    Many thanks for your reply and nice explanations. Actually I think of using another approach, but I will also try it. Voted+ – Jack Aug 18 '15 at 12:42
  • 4
    I think this is a much cleaner approach – niico Nov 13 '17 at 17:01
  • 5
    In addition to the Startup.Auth.cs class you need to copy the Startup.cs located at the root of the sample project. – Padmika Nov 18 '17 at 10:56
  • 2
    Shyamal can you add the Startup.cs from @Padmika's comment? This is important. – Mike Feb 19 '20 at 14:03
4

I recommend IdentityServer.This is a .NET Foundation project and covers many issues about authentication and authorization.

Overview

IdentityServer is a .NET/Katana-based framework and hostable component that allows implementing single sign-on and access control for modern web applications and APIs using protocols like OpenID Connect and OAuth2. It supports a wide range of clients like mobile, web, SPAs and desktop applications and is extensible to allow integration in new and existing architectures.

For more information, e.g.

  • support for MembershipReboot and ASP.NET Identity based user stores
  • support for additional Katana authentication middleware (e.g. Google, Twitter, Facebook etc)
  • support for EntityFramework based persistence of configuration
  • support for WS-Federation
  • extensibility

check out the documentation and the demo.

TotPeRo
  • 6,561
  • 4
  • 47
  • 60
  • 6
    The practical usages of IdentityServer should be considered before blindly jumping into an IdentityServer implementation. – hanzolo Jan 12 '18 at 21:20
1

well, I know I am probably too late. This is for those who already did one or many migration. those that there project works perfectly, those who have the AspNet tables in there database , but don't have controllers, models and view related to those.
I also cam across the same issue. I started my project without activating the authentication at the beginning. Then I realized I did no have all the element for the authentication(Account and Manage in the Views folder, accountController and ManageControler in the controller, and AccountViewModel and ManageViewModel in the Model). I just created and other project with similar settings, name and I activated the authentication at the creation of that project. then I manage to copie the missing file to my initial project. After that, I went through each of then to change the namespaces and imports to the namespace of my project

LouisIs
  • 11
  • 1