Asp.net web API 2 authentication with asp.net Identity - c#

I have Solution consisting of 3 projects, first is Data Access Layer (where models and Database Context are defined), and other two are Asp.net MVC 5 and Asp.net Web Api 2 projects.
For authentication and authorization I'm using Asp.net Identity which I've set up like this (in my DAL project):
public class DatabaseContext : IdentityDbContext<User>
{
public DatabaseContext()
: base("DefaultConnection")
{
Configuration.LazyLoadingEnabled = true;
}
public DbSet<IdentityUser> IdentityUsers { get; set; }
/*
Other DbSets ...
*/
}
My User class extends IdentityUser.
In my ASP.net project I've changed a bit Account controller so it works with my Database Context, here are relevant parts which I changed:
[Authorize]
public class AccountController : Controller
{
public AccountController()
: this(new UserManager<User>(new UserStore<User>(new DatabaseContext())))
{
}
public AccountController(UserManager<User> userManager)
{
UserManager = userManager;
}
public UserManager<User> UserManager { get; private set; }
}
And this part works fine. So my question is, what changes I have to do in order my Web API project works with my entity User and my Database Context, so in order to consume API user must be logged in ? (and of-course user has option to register via API).
I.E. I want to enable authorization and authentication for web api 2 by using my Database Context and class User which extends Identity User class.
UPDATE
What I've tried to do is this: I've replaced all IdentityUser classes in my web api 2 project with my class User, but on line (when I try to log in):
User user = await userManager.FindAsync(context.UserName, context.Password);
I get error:
System.Data.SqlClient.SqlException: Invalid object name 'dbo.AspNetUsers'.
This approach worked for my asp.net MVC project.

In short what I did was:
In my DatabaseContext class I've added these lines:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>()
.ToTable("AspNetUsers");
modelBuilder.Entity<User>()
.ToTable("AspNetUsers");
}
In asp.net web api project I've changed all classes IdentityUser to User, and that's it

Related

Asp MVC: How get roles from ApplicationUser

In ASP.NET MVC 5, in a controller, I have take the user that has make the request with:
ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
With the ApplicationUser instance, how can i get all the Roles of the user?
You can get user and assigned roles by using UserManager.
var userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
and then you can get your user like you already did, and also you can get roles for particular user by calling GetRoles method
userManager.GetRoles(userId);
List<string> roles = new UserManager().GetRoles(userIdString)).ToList();
below needed classes were created automatically in ASP.NET 4.5 project using VS 2015 using . the file name is IdentityModels.cs.
default 4 Nuget packages are installed including Microsoft.AspNet.WebApi v5.2.3
public class UserManager : UserManager<ApplicationUser>
{
public UserManager()
: base(new UserStore<ApplicationUser>(new ApplicationDbContext()))
{
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
public class ApplicationUser : IdentityUser
{
}

DB-First authentication confusion with ASP.NET Web API 2 + EF6

I need to create a Web API C# application for an existing MySQL database. I've managed to use Entity Framework 6 to bind every database table to a RESTful API (that allows CRUD operations).
I want to implement a login/registration system (so that I can implement roles and permissions in the future, and restrict certain API requests).
The MySQL database I have to use has a table for users (called user) that has the following self-explanatory columns:
id
email
username
password_hash
It seems that the de-facto standard for authentication is ASP.Net Identity. I have spent the last hour trying to figure out how to make Identity work with an existing DB-First Entity Framework setup.
If I try to construct ApplicationUser instances storing user instances (entities from the MySQL database) to retrieve user data, I get the following error:
The entity type ApplicationUser is not part of the model for the current context.
I assume I need to store Identity data in my MySQL database, but couldn't find any resource on how to do that. I've tried completely removing the ApplicationUser class and making my user entity class derive from IdentityUser, but calling UserManager.CreateAsync resulted in LINQ to Entities conversion errors.
How do I setup authentication in a Web API 2 application, having an existing user entity?
You say:
I want to implement a login/registration system (so that I can
implement roles and permissions in the future, and restrict certain
API requests).
How do I setup authentication in a Web API 2 application, having an
existing user entity?
It definitely means that you DO NOT need ASP.NET Identity. ASP.NET Identity is a technology to handle all users stuffs. It actually does not "make" the authentication mechanism. ASP.NET Identity uses OWIN Authentication mechanism, which is another thing.
What you are looking for is not "how to use ASP.NET Identity with my existing Users table", but "How to configure OWIN Authentication using my existing Users table"
To use OWIN Auth follow these steps:
Install the packages:
Owin
Microsoft.AspNet.Cors
Microsoft.AspNet.WebApi.Client
Microsoft.AspNet.WebApi.Core
Microsoft.AspNet.WebApi.Owin
Microsoft.AspNet.WebApi.WebHost
Microsoft.Owin
Microsoft.Owin.Cors
Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.Security
Microsoft.Owin.Security.OAuth
Create Startup.cs file inside the root folder (example):
make sure that [assembly: OwinStartup] is correctly configured
[assembly: OwinStartup(typeof(YourProject.Startup))]
namespace YourProject
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
//other configurations
ConfigureOAuth(app);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/security/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
Provider = new AuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
try
{
//retrieve your user from database. ex:
var user = await userService.Authenticate(context.UserName, context.Password);
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
//roles example
var rolesTechnicalNamesUser = new List<string>();
if (user.Roles != null)
{
rolesTechnicalNamesUser = user.Roles.Select(x => x.TechnicalName).ToList();
foreach (var role in user.Roles)
identity.AddClaim(new Claim(ClaimTypes.Role, role.TechnicalName));
}
var principal = new GenericPrincipal(identity, rolesTechnicalNamesUser.ToArray());
Thread.CurrentPrincipal = principal;
context.Validated(identity);
}
catch (Exception ex)
{
context.SetError("invalid_grant", "message");
}
}
}
}
Use the [Authorize] attribute to authorize the actions.
Call api/security/token with GrantType, UserName, and Password to get the bearer token. Like this:
"grant_type=password&username=" + username + "&password=" password;
Send the token within the HttpHeader Authorization as Bearer "YOURTOKENHERE". Like this:
headers: { 'Authorization': 'Bearer ' + token }
Hope it helps!
Since your DB schema are not compatible with default UserStore You must implement your own UserStore and UserPasswordStore classes then inject them to UserManager. Consider this simple example:
First write your custom user class and implement IUser interface:
class User:IUser<int>
{
public int ID {get;set;}
public string Username{get;set;}
public string Password_hash {get;set;}
// some other properties
}
Now author your custom UserStore and IUserPasswordStore class like this:
public class MyUserStore : IUserStore<User>, IUserPasswordStore<User>
{
private readonly MyDbContext _context;
public MyUserStore(MyDbContext context)
{
_context=context;
}
public Task CreateAsync(AppUser user)
{
// implement your desired logic such as
// _context.Users.Add(user);
}
public Task DeleteAsync(AppUser user)
{
// implement your desired logic
}
public Task<AppUser> FindByIdAsync(string userId)
{
// implement your desired logic
}
public Task<AppUser> FindByNameAsync(string userName)
{
// implement your desired logic
}
public Task UpdateAsync(AppUser user)
{
// implement your desired logic
}
public void Dispose()
{
// implement your desired logic
}
// Following 3 methods are needed for IUserPasswordStore
public Task<string> GetPasswordHashAsync(AppUser user)
{
// something like this:
return Task.FromResult(user.Password_hash);
}
public Task<bool> HasPasswordAsync(AppUser user)
{
return Task.FromResult(user.Password_hash != null);
}
public Task SetPasswordHashAsync(AppUser user, string passwordHash)
{
user.Password_hash = passwordHash;
return Task.FromResult(0);
}
}
Now you have very own user store simply inject it to the user manager:
public class ApplicationUserManager: UserManager<User, int>
{
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new MyUserStore(context.Get<MyDbContext>()));
// rest of code
}
}
Also please note you must directly inherit your DB Context class from DbContext not IdentityDbContext since you have implemented own user store.

ASP.NET vNext EF7 dbContext issues

I am starting a vNext project, and I'm having some issues kicking it off the ground. I have added a table to the ApplicationDbContext class, and it successfully created the table in the db (which in my case is in Azure). However, I can't seem to correctly instantiate a dbContext to use in my Controllers.
In my experience with previous ASP.NET EF projects, I could instantiate the ApplicationDbContext class without passing it any parameters, but in the case of vNext however, it seems to expect a number of things (IServiceProvider, and IOptionsAccessor<DbContextOptions>). I have tried creating a parameter-less constructor, but the App breaks due to not knowing what connection strings to use. My code is below -- as you see in the OnConfiguring(DbContextOptions options) override, I force the connection string in via the DbContextOptions, but that's obviously not ideal, and I feel like I'm just not understanding where those two IServiceProvider, and IOptionsAccessor parameters need to come from.
Thanks for any help!
namespace Project.Models
{
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser
{
public string CompanyName { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
private static bool _created = false;
public DbSet<Business> Businesses { get; set; }
public ApplicationDbContext()
: base()
{
if (!_created)
{
Database.EnsureCreated();
_created = true;
}
}
protected override void OnConfiguring(DbContextOptions options)
{
var configuration = new Configuration();
configuration.AddJsonFile("config.json");
configuration.AddEnvironmentVariables();
options.UseSqlServer(configuration.Get("Data:DefaultConnection:ConnectionString"));
}
public ApplicationDbContext(IServiceProvider serviceProvider, IOptionsAccessor<DbContextOptions> optionsAccessor)
: base(serviceProvider, optionsAccessor.Options)
{
// Create the database and schema if it doesn't exist
// This is a temporary workaround to create database until Entity Framework database migrations
// are supported in ASP.NET vNext
if (!_created)
{
Database.EnsureCreated();
_created = true;
}
}
}
}
IServiveProvider and IOptionAccessor are injected by the Dependency Injection
the ASP.Net Core DI has limitation, you cannot have more than one constructor.
Read this: http://blogs.msdn.com/b/webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx

The entity type IdentityUser is not part of the model for the current context

I see the same issue as this question, but the scenario presented there doesn't seem to apply so I think I have a different issue. In fact, I'm seeing several questions on SO that are similar, each with different causes and solutions, so I think this error must be caused from a high level. That said...
I have an EF code-first database model and I'm trying to use IdentityUser to extend a standard registration for my MVC 5 site.
I have my extended UserModel:
namespace MyMvcSite.Models
{
public class UserModel : IdentityUser
{
public string BillingId { get; set; }
public virtual ICollection<DatabaseModel> Databases { get; set; }
}
}
And my context:
using MyMvcSite.Models;
namespace MyMvcSite.Web
{
public class AuthContext : IdentityDbContext<UserModel>
{
public AuthContext() : base("AuthContext") { }
}
}
Now, when I execute the code to register a user:
public async Task<IdentityResult> RegisterUser(UserModel user)
{
user.Email = user.UserName;
var result = await _userManager.CreateAsync(user);
return result;
}
I get this error:
The entity type IdentityUser is not part of the model for the current context.
I can't figure out what this error means, because it looks like I have everything correct. Can anyone tell what might be going wrong???
I know my connectionString AuthContext is correct because I have used it previously.
When you are using a custom user class with ASP.NET Identity, you have to make sure that you explicitly specify the custom user class type <T> to both the UserManager and the UserStore on instantiation.
private UserManager<UserModel> _userManager;
public AccountController()
{
AuthContext _ctx = new AuthContext();
UserStore<UserModel> userStore = new UserStore<UserModel>(_ctx);
_userManager = new UserManager<UserModel>(userStore);
}
or in shorter form (like your reply comment):
private UserManager<UserModel> _userManager;
public AccountController()
{
AuthContext _ctx = new AuthContext();
_userManager = new UserManager<UserModel>(new UserStore<UserModel>(_ctx));
}
If the type is allowed to defaulted to IdentityUser when you want to use a custom class you will experience the error you reported.
I was having this same problem, and I recall having a similar problem working with SimpleMembership in MVC4.
I’m doing database first development, so I have an EDMX file. Turns out, ASP.NET Identity does not like the connection string that is created when you generate your .edmx model file. If you are using a. EDM connection string in :base(“EDMConnString”) you will most likely have this problem.
I fixed this by creating a standard connection string that pointed to the database where the ASP.NET Identity tables are (in my case the same database), used that connection string in :base, and it worked.
Something like this
<add name="IdentityConnection" connectionString="data source=THEJUS\SQLSERVER2014;initial catalog=IdentitySample;integrated security=True;MultipleActiveResultSets=True;App=IdentitySample.Admin" providerName="System.Data.SqlClient" />
I got this error when I introduced DI to my project. Using AutoFac and Identity I had to add the following: builder.RegisterType<ApplicationDbContext>().As<DbContext>().InstancePerLifetimeScope();
Without this, when AutoFac was creating my UserStore instance, it was using the default UserStore() ctor which creates a new IdentityDbContext, not my ApplicationDbContext.
With this line, UserStore(DbContext context) ctor gets called, with my ApplicationDbContext.
Here is some step i figured out while resolving this issue
First Check your database for Table of ASP.Net Identity
Create these table on your database if not exist you can also apply migration
Check the below image and verify your code
Register Action
IdentityDbContext Class

Asp.NET Identity IdentityUser Custom Properties Cause Runtime Error

I am writing an application using the VS2013 SPA template that includes Asp.NET Identity, WebAPI2, KnockoutJS and SqlServer Express 2012.
I started off using the IdentityUser class to handle my users and that worked just fine. I was able to add and login as a user with no problem. I then wanted to add custom information to the IdentityUser (there was an article I can no longer find).
As a result, I created an User class that inherited from IdentityUser as seen below.
public class User : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Then I updated all the references in the project from IdentityUser to User.
Now, whenever I try to login I get the following error:
The entity type User is not part of the model for the current context.
The thing is, I have a DBInitializer (public class ApplicationDBInitializer : DropCreateDatabaseAlways<ApplicationDBContext>) that always recreates the database and adds some test users and the tables are created and the users are added successfully.
On the off chance it matters, here is my cxn string: <add name="DefaultConnection" connectionString="Server=.\SQLEXPRESS;Database=ibcf;Trusted_Connection=True;" providerName="System.Data.SqlClient" />
and my DBContext
public class ApplicationDBContext : IdentityDbContext<User>
{
public ApplicationDBContext()
: base("DefaultConnection")
{
}
}
Why is this error happening?
The issue was that the Startup.Auth.cs continued to reference the default IdentityDbContext<User> DB context. After updating the class to reference my ApplicationDBContext the issue was resolved.
static Startup()
{
...
UserManagerFactory = () => new UserManager<User>(new UserStore<User>(new ApplicationDBContext()));
...
}

Categories