locked
Token Authentication With Existing Database RRS feed

  • Question

  • User1122355199 posted

    Hello everyone and thanks for the help in advance.  I am trying to develop a WebApi that uses token authentication and an existing user database.  I have been working through the example and sample code located at http://www.sangadjiprabowo.com/post/2017/03/12/customizing-token-based-authentication-oauth-in-asp-net-web-api-with-existing-user-database.  I am at the early stages an am simply trying to register a user, but am running into problems.  Here is where I am at.  I have a model:

            [Required]
            [Display(Name = "User name")]
            public string UserName { get; set; }
    
            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }
    
            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.")]
            public string FirstName { get; set; }
    
            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.")]
            public string LastName { get; set; }
    
            [Required]
            public DateTime BirthDate { get; set; }

    which corresponds to a table called UserModel in MyDatabase.  The context looks like:

        public class OAuthContext : IdentityDbContext<IdentityUser>
        {
    
            public OAuthContext() : base("name=MyConnectionString")
            {
            }
    
            public static OAuthContext Create()
            {
                return new OAuthContext();
            }
    
        }

    MyConnectionString is in the connection string section in web.config and pints to the custom database.  The api controller for registration:

            [AllowAnonymous]
            [HttpPost]
            [Route("api/Registration/Register")]
            public async Task<IHttpActionResult> Register(UserModel model)
            {
                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }
    
                using (var repository = new AuthRepository())
                {
                    IdentityResult result = await repository.RegisterUser(model);
    
                    if (!result.Succeeded)
                    {
                        //return GetErrorResult(result);
                    }
    
                    return Ok();
                }
            }

    Finally AuthRepository:

        public class AuthRepository : IDisposable
        {
            private OAuthContext authContext;
            private UserManager<IdentityUser> userManager;
    
            public AuthRepository()
            {
                authContext = new OAuthContext();
                userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(authContext));
            }
    
            public async Task<IdentityResult> RegisterUser(UserModel userModel)
            {
                IdentityUser user = new IdentityUser
                {
                    UserName = userModel.UserName
                };
    
                var result = await userManager.CreateAsync(user, userModel.Password);
                
                return result;
            }
    
            public async Task<IdentityUser> FindUser(string userName, string password)
            {
                return await userManager.FindAsync(userName, password);
            }
    
            public void Dispose()
            {
                authContext.Dispose();
                userManager.Dispose();
            }
        }

    When I make a jQuery post:

                    var jqxhr = $.post('../api/Registration/Register', {
    
                        "UserName": "User",
                        "Password": "Password",
                        "FirstName": "Tom",
                        "LastName": "Test",
                        "BirthDate": "2019-02-15T00:00:00.0000000-00:00"
                    }

    a 200 "OK" is returned, however, nothing is inserted into the UserModel table in the database.  I'm not sure where to go from here since I'm new to these concepts.  Any help would be appreciated.

    Friday, October 4, 2019 8:28 PM

Answers

  • User61956409 posted

    Hi kmcnet,

    I tried registering another user and no error was thrown.  So the user is being added, however, not to the table I am targeting.  

    You specified connection string name in your DbContext class, does it still not add the user you register to AspNetUsers table in targeting database you specified? Or you want to add new register user in a new table (not AspNetUsers table) in targeting database?

    With Regards,

    Fei Han

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, October 8, 2019 5:47 AM
  • User475983607 posted

    kmcnet

    MG seemed to indicate the code example I'm working on wouldn't work.

    I read the tutorial and thought it indicated the records are saved in dbo.Users when the table is actually AspNetUser.  I though you were looking in the wrong table since it was clear the records were being stored.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, October 8, 2019 9:48 PM

All replies

  • User475983607 posted

    I'm not sure where to go from here since I'm new to these concepts. 

    Your code ignores registration errors.

    if (!result.Succeeded)
    {
    	//return GetErrorResult(result);
    }
    

    What's stopping you from setting a breakpoint and do a little debugging?

    Friday, October 4, 2019 9:15 PM
  • User1122355199 posted

    I appreciate the response, and yes, I did insert breakpoints prior to posting.  I'm probably not being clear enough in my post.  When I enable registration errors, the breakpoint returns "Name User is already taken.".  So obviously, this is being registered to the default database.  To be completely honest, I don't know how to use a breakpoint to determine what database the context is connected to.

    Saturday, October 5, 2019 1:47 AM
  • User475983607 posted

    I appreciate the response, and yes, I did insert breakpoints prior to posting.  I'm probably not being clear enough in my post.  When I enable registration errors, the breakpoint returns "Name User is already taken.".  So obviously, this is being registered to the default database.  To be completely honest, I don't know how to use a breakpoint to determine what database the context is connected to.

    You should always post the error message so the community is not guessing.  The connection string is commonly in the configuration file, web.config or appsetting.json depending on the .NET framework you are targeting.  You can also use the find feature in Visual Studio to find the DB string.

    A few recommendations.  Drop the repository pattern.  Entity Framework implements a repository pattern and Identity is an API (service).  You'll end up having to add a method for each UserManager,or SignInManager method you wish to use.  It's much easier use the Identity APIs and EF as is.  

    Saturday, October 5, 2019 1:01 PM
  • User1122355199 posted

    The connection string is commonly in the configuration file, web.config or appsetting.json depending on the .NET framework you are targeting. 

    If you look at my initial post, I state I set the connection string in my web.config file, then call it:

        public class OAuthContext : IdentityDbContext<IdentityUser>
        {
    
            public OAuthContext() : base("name=MyConnectionString")
            {
            }
    
            public static OAuthContext Create()
            {
                return new OAuthContext();
            }
    
        }

    So I believe I am pointing to the correct database, but obviously, I am doing something wrong or not understanding how this is supposed to work.  As I said earlier, I set up a table in the targeted database named UserModel.  However, no records are entered into this table.  However, the error message indicates a duplicate user exists.  

    Saturday, October 5, 2019 1:46 PM
  • User475983607 posted

    As I said earlier, I set up a table in the targeted database named UserModel.  However, no records are entered into this table.  However, the error message indicates a duplicate user exists.

    Your response seemed to indicate you did not know what database your connected to.  Honestly, your posts are very confusing.  The subject is tokens but it seems the issue is using the SignInManager. 

    To be completely honest, I don't know how to use a breakpoint to determine what database the context is connected to.

    This error happens when the SignInManager queried the database and founds a matching username.  My best guess is you are looking in the wrong database.  Perhaps take a look at the connection string and verify looking in the same database your application connects to.  Or perhaps try a different username and see if the error changes or the insert is successful.

    Saturday, October 5, 2019 3:57 PM
  • User1122355199 posted

    I appreciate the response and the help.  I don't think I can be more clear in my post, but we do seem to have communication issues and I will try to provide better explanation.  I am learning/building a webapi that needs to be secured.  After reading about this topic, it appears token authentication is the most secure and state of the art.  For reasons outside of my control, I cannot use the default identity database and instead must integrate the security with a current database.  While researching, I found the article http://www.sangadjiprabowo.com/post/2017/03/12/customizing-token-based-authentication-oauth-in-asp-net-web-api-with-existing-user-database which I am trying to use as a template.  As I stated earlier, I set up the OAuthContext to my database:

        public class OAuthContext : IdentityDbContext<IdentityUser>
        {
    
            public OAuthContext() : base("name=MyConnectionString")
            {
            }
    
            public static OAuthContext Create()
            {
                return new OAuthContext();
            }
    
        }
    }

    As you noted earlier, I created the AuthRepository

        public class AuthRepository : IDisposable
        {
            private OAuthContext authContext;
            private UserManager<IdentityUser> userManager;
    
            public AuthRepository()
            {
                authContext = new OAuthContext();
                userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(authContext));
            }
    
            public async Task<IdentityResult> RegisterUser(UserModel userModel)
            {
                 IdentityUser user = new IdentityUser
                {
                    UserName = userModel.UserName
                };
    
                var result = await userManager.CreateAsync(user, userModel.Password);
                
                return result;
            }
    
            public async Task<IdentityUser> FindUser(string userName, string password)
            {
                return await userManager.FindAsync(userName, password);
            }
    
            public void Dispose()
            {
                authContext.Dispose();
                userManager.Dispose();
            }
    
    
        }

    which makes use of the OAuthContext.  I created a separate RegistrationController:

        public class RegistrationController : ApiController
        {
            [AllowAnonymous]
            [HttpPost]
            [Route("api/Registration/Register")]
            public async Task<IHttpActionResult> Register(UserModel model)
            {            
                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }
    
                using (var repository = new AuthRepository())
                {
                    IdentityResult result = await repository.RegisterUser(model);
    
                    if (!result.Succeeded)
                    {
                        return GetErrorResult(result);
                    }
    
                    return Ok();
                }
            }
            [HttpGet]
            [Route("api/Registration/Helper")]
            public string Helper()
            {
                return "OK";
            }
    
            private IHttpActionResult GetErrorResult(IdentityResult result)
            {
                if (result == null)
                {
                    return InternalServerError();
                }
    
                if (!result.Succeeded)
                {
                    if (result.Errors != null)
                    {
                        foreach (string error in result.Errors)
                        {
                            ModelState.AddModelError("", error);
                        }
                    }
    
                    if (ModelState.IsValid)
                    {
                        // No ModelState errors are available to send, so just return an empty BadRequest.
                        return BadRequest();
                    }
    
                    return BadRequest(ModelState);
                }
    
                return null;
            }
        }

    that calls the AuthRepository which in turn utilizes OAuthContext which has the connection string to the production database.  The AuthRepository makes no use of the SignIn Manager to my knowledge, instead using its own registration.  I started testing this code by simply trying to register a user.  I haven't even gotten to signing in and authenticating.  However, when a user is registered, I do not see the user in the UserModel table of the database.  However, to your point in your last post, I tried registering another user and no error was thrown.  So the user is being added, however, not to the table I am targeting.  

    Saturday, October 5, 2019 8:31 PM
  • User475983607 posted

    kmcnet

    However, to your point in your last post, I tried registering another user and no error was thrown.  So the user is being added, however, not to the table I am targeting.  

    As we go along and do a bit of troubleshooting we find the account is registered (saved) but the records are not in the expected table.  Where are the records saved?  

    The code that registers user looks like you are using the UserManager.  Sorry, I wrote SingInManager above.

            public async Task<IdentityResult> RegisterUser(UserModel userModel)
            {
                IdentityUser user = new IdentityUser
                {
                    UserName = userModel.UserName
                };
    
                var result = await userManager.CreateAsync(user, userModel.Password);
                
                return result;
            }

    By default the user manager saves user accounts in the AspNetUser table.  Where did you expect the user account to show up?

    Edit: I'm pretty sure the tutorial does not work as advertised which is the source of the confusion. 

    Saturday, October 5, 2019 10:04 PM
  • User1122355199 posted

    I'm expecting it to appear in my specified database because...

        public class OAuthContext : IdentityDbContext<IdentityUser>
        {
    
            public OAuthContext() : base("name=MyConnectionString")
            {
            }
    
            public static OAuthContext Create()
            {
                return new OAuthContext();
            }
    
        }

    points to my database and not the default database.  When AuthRepository is called:

            private OAuthContext authContext;
            private UserManager<IdentityUser> userManager;
    
            public AuthRepository()
            {
                authContext = new OAuthContext();
                userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(authContext));
            }

    it should be linked to the authConext which points to my specified database, not the default.  What am I missing?

    BTW, yes this is still about token authentication, but I need to get the application pointing to the right database first.

    Saturday, October 5, 2019 11:23 PM
  • User61956409 posted

    Hi kmcnet,

    I tried registering another user and no error was thrown.  So the user is being added, however, not to the table I am targeting.  

    You specified connection string name in your DbContext class, does it still not add the user you register to AspNetUsers table in targeting database you specified? Or you want to add new register user in a new table (not AspNetUsers table) in targeting database?

    With Regards,

    Fei Han

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, October 8, 2019 5:47 AM
  • User1122355199 posted

    Thanks for the response.  The registration was being inserted into the default database when I was trying to target a specified database.  I changed the code:

    public ApplicationDbContext()
                : base("name=MyConnectionString", throwIfV1Schema: false)

    To:

    public ApplicationDbContext()
                : base("MyConnectionString", throwIfV1Schema: false)

    Which now seems to direct the AspNetUsers table in the correct database.  So the accounts now seem to be created in the correct place.  So I'm now moving to the token authentication portion of the project.  MG seemed to indicate the code example I'm working on wouldn't work.  

    Tuesday, October 8, 2019 9:13 PM
  • User475983607 posted

    kmcnet

    MG seemed to indicate the code example I'm working on wouldn't work.

    I read the tutorial and thought it indicated the records are saved in dbo.Users when the table is actually AspNetUser.  I though you were looking in the wrong table since it was clear the records were being stored.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, October 8, 2019 9:48 PM
  • User-2017229834 posted

    You are using EntityFramework Code First and Asp.Net Identity User manager. Hope you have given the right connectionstring and Db Name.

    In this case when you register a new user first time it will generate database itself (given in name in ConnectionString) and Few tables

    [dbo].[AspNetRoles]
    [dbo].[AspNetUserClaims]
    [dbo].[AspNetUserLogins]
    [dbo].[AspNetUserRoles]
    [dbo].[AspNetUsers]

    Thursday, October 10, 2019 7:16 AM
  • User1122355199 posted

    Thanks for the response.  You are correct in that I also misread the article and initially thought it would be saved in dbo.users instead of AspNetUser.  Once I removed the default connection string, the AspNetuser table was created in the correct database with the user data.

    Tuesday, October 15, 2019 2:23 AM