{"id":667,"date":"2019-05-29T14:24:18","date_gmt":"2019-05-29T18:24:18","guid":{"rendered":"http:\/\/salzlechner.com\/dev\/?p=667"},"modified":"2019-08-10T11:57:35","modified_gmt":"2019-08-10T15:57:35","slug":"net-core-web-api-part-2","status":"publish","type":"post","link":"http:\/\/salzlechner.com\/dev\/2019\/05\/29\/net-core-web-api-part-2\/","title":{"rendered":".NET Core Web API &#8211; Part 2"},"content":{"rendered":"<p>In this second part we will start adding an endpoint for authentication to our web API.<\/p>\n<p>To do this we need to add a new controller. We will add an empty API controller and call it AccountsController.<\/p>\n<p>We will use dependency injection to add a UserManager, a SignInManager and a ILogger instance.<\/p>\n<pre class=\"lang:default decode:true \">[Route(\"api\/[controller]\")]\n[ApiController]\npublic class AccountsController : ControllerBase\n{\n    private readonly UserManager&lt;ApplicationUser&gt; _userManager;\n    private readonly SignInManager&lt;ApplicationUser&gt; _signInManager;\n    private readonly ILogger _logger;\n\n    public AccountsController(\n                UserManager&lt;ApplicationUser&gt; userManager,\n                SignInManager&lt;ApplicationUser&gt; signInManager,\n                ILogger&lt;AccountsController&gt; logger)\n    {\n        _userManager = userManager;\n        _signInManager = signInManager;\n        _logger = logger;\n    }\n}\n<\/pre>\n<p>We also create a new model class to pass login information to our authentication method<\/p>\n<pre class=\"lang:default decode:true \">public class LoginModel\n{\n    [Required]\n    [EmailAddress]\n    public string Email { get; set; }\n\n    [Required]\n    [DataType(DataType.Password)]\n    public string Password { get; set; }\n\n    [Display(Name = \"Remember me?\")]\n    public bool RememberMe { get; set; }\n}\n<\/pre>\n<p>last but not least we will add out authentication method to the Accounts controller as an HTTP POST method.<\/p>\n<p>We will allow anonymous access to this endpoint of course.<\/p>\n<pre class=\"lang:default decode:true \">[HttpPost]\n[AllowAnonymous]        \n[Route(\"Authenticate\")]\npublic async Task&lt;IActionResult&gt; Authenticate([FromBody] LoginModel model)\n{\n    if (ModelState.IsValid)\n    {\n        var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);\n        if (result.Succeeded)\n        {\n            _logger.LogInformation(\"User logged in.\");\n            return Ok(model);\n        }\n        if (result.IsLockedOut)\n        {\n            return BadRequest(\"User locked out\");\n        }\n        else\n        {\n            return BadRequest(\"Invalid User or Password\");\n        }\n    }\n\n    return BadRequest(\"Invalid User or Password\");\n}\n<\/pre>\n<p>calling the Authenticate method using HTTP Post with the following data in the body<\/p>\n<pre class=\"lang:default decode:true \">{\n\tEmail: \"admin@localhost\",\n\tPassword: \"Admin!1234\"\n}<\/pre>\n<p>will return a status 200 and also a application cookie to be used for authentication.<\/p>\n<p>After that we should be able to call our method on the values controller that we decorated with the&nbsp;Authorize keyword.<\/p>\n<p>Now we will add another endpoint to the Accounts controller that will allow us to add new users to the system<\/p>\n<p>This will be another HTTP Post method that will only be allowed for users that are part of the Administrator role.<\/p>\n<p>In some systems you may want to allow anonymous access if users can register themselves in the system.<\/p>\n<p>First we create a model for the registration data<\/p>\n<pre class=\"lang:default decode:true\">public class RegisterModel\n{\n    [Required]\n    [EmailAddress]\n    [Display(Name = \"Email\")]\n    public string Email { get; set; }\n\n    [Required]\n    [StringLength(100, ErrorMessage = \"The {0} must be at least {2} and at max {1} characters long.\", MinimumLength = 6)]\n    [DataType(DataType.Password)]\n    [Display(Name = \"Password\")]\n    public string Password { get; set; }\n\n    [DataType(DataType.Password)]\n    [Display(Name = \"Confirm password\")]\n    [Compare(\"Password\", ErrorMessage = \"The password and confirmation password do not match.\")]\n    public string ConfirmPassword { get; set; }\n}\n<\/pre>\n<p>and then we add the Register method to the AccountsController<\/p>\n<pre class=\"lang:default decode:true\">[HttpPost]\n[Authorize(Roles = \"Administrator\")]  \n[Route(\"Register\")]\npublic async Task&lt;IActionResult&gt; Register([FromBody] RegisterModel model)\n{\n\n    if (ModelState.IsValid)\n    {\n        var user = new ApplicationUser { UserName = model.Email, Email = model.Email };\n        var result = await _userManager.CreateAsync(user, model.Password);\n        if (result.Succeeded)\n        {\n            _logger.LogInformation(\"User created a new account with password.\");\n\n            return Ok(result);\n        }\n        else\n        {\n            return BadRequest(result);\n        }\n    }\n\n    return BadRequest(\"Failed\");\n}\n<\/pre>\n<p>to allow access only by users belonging to the Administrator role we decorate the method with the Authorize decorator and assign the Roles.<\/p>\n<p>After validating the model we create a new user and return either success or the error information<\/p>\n<p>Next lets add an endpoint to allow the user to change his or her password.<\/p>\n<pre class=\"lang:default decode:true \">[HttpPost]\n[Route(\"ChangePassword\")]\npublic async Task&lt;IActionResult&gt; ChangePassword([FromBody] ChangePasswordModel model)\n{\n\n    if (ModelState.IsValid)\n    {\n        var user = await _userManager.GetUserAsync(HttpContext.User);\n\n        if (user == null)\n        {\n            return NotFound();\n        }\n\n        var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword );\n        if (result.Succeeded)\n        {\n            _logger.LogInformation(\"User changed password.\");\n\n            return Ok(result);\n        }\n        else\n        {\n            return BadRequest(result);\n        }\n    }\n\n    return BadRequest(\"Failed\");\n}\n<\/pre>\n<p>this will allow the user to change the password. The old password has to be known to make the change.<\/p>\n<p>But what if an administrator would need to change the password for a user without having access to the old password.<\/p>\n<p>In order to handle that requirement we add another endpoint called ChangePasswordForUser only accessible to users of the role Administrator<\/p>\n<pre class=\"lang:default decode:true \">[HttpPost]\n[Route(\"ChangePasswordForUser\")]\n[Authorize(Roles = \"Administrator\")]\npublic async Task&lt;IActionResult&gt; ChangePasswordForUser([FromBody] ChangePasswordForUserModel model)\n{\n\n    if (ModelState.IsValid)\n    {\n        var user = await _userManager.FindByNameAsync(model.UserName);\n        if (user == null) {\n            return NotFound();\n        }\n\n        \/\/ compute the new hash string\n        var newPassword = _userManager.PasswordHasher.HashPassword(user, model.NewPassword);\n        user.PasswordHash = newPassword;\n        var res = await _userManager.UpdateAsync(user);\n\n        if (res.Succeeded) {\n            _logger.LogInformation(\"Password for User changed.\");\n\n            return Ok(res);\n        }\n        else {\n            return BadRequest(res);\n        }\n        \n    }\n\n    return BadRequest(\"Failed\");\n}\n<\/pre>\n<p>This concludes part 2 of this series. In Part 3 we will be looking into additional features for the account management.<\/p>\n\n\t\t<div class='author-shortcodes'>\n\t\t\t<div class='author-inner'>\n\t\t\t\t<div class='author-image'>\n\t\t\t<img src='http:\/\/salzlechner.com\/dev\/wp-content\/uploads\/sites\/2\/2016\/02\/mike5crop-566174_60x60.jpg' alt='' \/>\n\t\t\t<div class='author-overlay'><\/div>\n\t\t<\/div> \n\t\t<div class='author-info'>\n\t\t\tMichael Salzlechner is the CEO of StarZen Technologies, Inc.<\/p>\n<p>He was part of the Windows Team at Data Access Worldwide that created the DataFlex for Windows Product before joining&nbsp;<a href=\"http:\/\/starzen.com\">StarZen Technologies<\/a>. StarZen Technologies provides consulting services as well as custom Application development and third party products<\/p>\n\t\t<\/div>\n\t\t\t<\/div>\n\t\t<\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this second part we will start adding an endpoint for authentication to our web API. To do this we need to add a new controller. We will add an empty API controller and call it AccountsController. We will use dependency injection to add a UserManager, a SignInManager and a ILogger instance. [Route(&#8220;api\/[controller]&#8221;)] [ApiController] public [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":735,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","ngg_post_thumbnail":0,"footnotes":""},"categories":[42,7],"tags":[],"class_list":["post-667","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net-core","category-c"],"_links":{"self":[{"href":"http:\/\/salzlechner.com\/dev\/wp-json\/wp\/v2\/posts\/667","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/salzlechner.com\/dev\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/salzlechner.com\/dev\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/salzlechner.com\/dev\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/salzlechner.com\/dev\/wp-json\/wp\/v2\/comments?post=667"}],"version-history":[{"count":4,"href":"http:\/\/salzlechner.com\/dev\/wp-json\/wp\/v2\/posts\/667\/revisions"}],"predecessor-version":[{"id":732,"href":"http:\/\/salzlechner.com\/dev\/wp-json\/wp\/v2\/posts\/667\/revisions\/732"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/salzlechner.com\/dev\/wp-json\/wp\/v2\/media\/735"}],"wp:attachment":[{"href":"http:\/\/salzlechner.com\/dev\/wp-json\/wp\/v2\/media?parent=667"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/salzlechner.com\/dev\/wp-json\/wp\/v2\/categories?post=667"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/salzlechner.com\/dev\/wp-json\/wp\/v2\/tags?post=667"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}