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

asp.net asp.net-web-api2 c# ef-database-first entity-framework

Question

I need to develop a Web API C# application for a MySQL database that already exists. I've been able to connect every database table to a RESTful API using Entity Framework 6 which enables CRUD operations.

I want to put in place a login/registration system.

The users (called user ) table in the MySQL database I must utilize has the following self-explanatory columns:

  • id
  • email
  • username
  • password_hash

ASP.Net Identity appears to be the de facto authentication standard. I've been attempting to integrate Identity with an existing DB-First Entity Framework configuration for the past hour.

If I put togetherApplicationUser situations storinguser I try to obtain user info from instance (Objects in the MySQL database), but I receive the following error:

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

I presume I must save Identity information in my MySQL database, but I was unable to locate any information on how to do it. I've attempted to remove theApplicationUser taking my class anduser the entity class comes fromIdentityUser however callingUserManager.CreateAsync error-filled LINQ to Entities conversions as a result.

How can I set up authentication in an existing Web API 2 application?user entity?

1
15
10/28/2015 6:34:10 PM

Accepted Answer

Say that:

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?

You obviously require ASP.NET Identity, as this indicates. A technology to manage all user data is ASP.NET Identity. In reality, it doesn't "make" the authentication system. Another problem is that OWIN Authentication is used by ASP.NET Identity.

You should be looking for "How to set up OWIN Authentication using the Users table I already have" instead of "How do I leverage my existing Users table with ASP.NET Identity?".

Please follow these procedures to use OWIN Auth:

Put the packages in place:

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 a file found in the root folder

ensure sure [assembly: OwinStartup] is configured properly

[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");
            }
        }
    }
}

Apply the[Authorize] attribute to give the actions permission.

Call api/security/token with GrantType , UserName , andPassword the bearer token to be obtained. akin to this

"grant_type=password&username=" + username + "&password=" password;

Send the token inside of theHttpHeader Authorization as Bearer "YOURTOKENHERE" . akin to this

headers: { 'Authorization': 'Bearer ' + token }

Hope it's useful!

23
9/12/2019 3:22:08 PM

Popular Answer

Since your database schema is incompatible with the standardUserStore You must put your own in place.UserStore and UserPasswordStore then inject them to classesUserManager . Consider this simple example:

Write your own unique user class first, then implementIUser interface:

class User:IUser<int>
{
    public int ID {get;set;}
    public string Username{get;set;}
    public string Password_hash {get;set;}
    // some other properties 
}

Write your custom now.UserStore and IUserPasswordStore class such as:

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);
    }
}

You may now insert your very own user store into the user manager as follows:

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
    }
}

Please be aware that your DB Context class must directly inherit fromDbContext not IdentityDbContext since you developed a user store of your own.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow