banner



How To Create Json Webservice In Asp Net

Introduction

Authentication is the process of validating user credentials and authorization is the process of checking privileges for a user to access specific modules in an application. In this article, we will see how to protect an ASP.NET Core Web API application by implementing JWT authentication. We will also see how to use authorization in ASP.NET Core to provide access to various functionality of the application. We will store user credentials in an SQL server database and we will use Entity framework and Identity framework for database operations.

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

In its compact form, JSON Web Tokens consist of three parts separated by dots (.), which are:

  • Header
  • Payload
  • Signature

Therefore, a JWT typically looks like the following.

xxxx.yyyy.zzzz

Please refer to below link for more details about JSON Web Tokens.


Create ASP.NET Core Web API using Visual Studio 2019

We can create an API application with ASP.NET Core Web API template.

We must install below libraries using NuGet package manager.

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.AspNetCore.Identity.EntityFrameworkCore
  • Microsoft.AspNetCore.Identity
  • Microsoft.AspNetCore.Authentication.JwtBearer

We can modify the appsettings.json with below values.

appsettings.json

  1. {
  2. "Logging" : {
  3. "LogLevel" : {
  4. "Default" : "Information" ,
  5. "Microsoft" : "Warning" ,
  6. "Microsoft.Hosting.Lifetime" : "Information"
  7.     }
  8.   },
  9. "AllowedHosts" : "*" ,
  10. "ConnectionStrings" : {
  11. "ConnStr" : "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=SarathlalDB;Integrated Security=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
  12.   },
  13. "JWT" : {
  14. "ValidAudience" : "http://localhost:4200" ,
  15. "ValidIssuer" : "http://localhost:61955" ,
  16. "Secret" : "ByYM000OLlMQG6VVVp1OH7Xzyr7gHuw1qvUC5dcGt3SNM"
  17.   }
  18. }

We have added a database connection string and also added valid audience, valid issuer and secret key for JWT authentication in above settings file.

Create an "ApplicationUser" class inside a new folder "Authentication" which will inherit the IdentityUser class. IdentityUser class is a part of Microsoft Identity framework. We will create all the authentication related files inside the "Authentication" folder.

ApplicationUser.cs

  1. using  Microsoft.AspNetCore.Identity;
  2. namespace  JWTAuthentication.Authentication
  3. {
  4. public class  ApplicationUser: IdentityUser
  5.     {
  6.     }
  7. }

We can create the "ApplicationDbContext" class and add below code.

ApplicationDbContext.cs

  1. using  Microsoft.AspNetCore.Identity.EntityFrameworkCore;
  2. using  Microsoft.EntityFrameworkCore;
  3. namespace  JWTAuthentication.Authentication
  4. {
  5. public class  ApplicationDbContext : IdentityDbContext<ApplicationUser>
  6.     {
  7. public  ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base (options)
  8.         {
  9.         }
  10. protected override void  OnModelCreating(ModelBuilder builder)
  11.         {
  12. base .OnModelCreating(builder);
  13.         }
  14.     }
  15. }

Create a static class "UserRoles" and add below values.

UserRoles.cs

  1. namespace  JWTAuthentication.Authentication
  2. {
  3. public static class  UserRoles
  4.     {
  5. public const string  Admin = "Admin" ;
  6. public const string  User = "User" ;
  7.     }
  8. }

We have added two constant values "Admin" and "User" as roles. You can add many roles as you wish.

Create class "RegisterModel" for new user registration.

RegisterModel.cs

  1. using  System.ComponentModel.DataAnnotations;
  2. namespace  JWTAuthentication.Authentication
  3. {
  4. public class  RegisterModel
  5.     {
  6.         [Required(ErrorMessage ="User Name is required" )]
  7. public string  Username { get ; set ; }
  8.         [EmailAddress]
  9.         [Required(ErrorMessage ="Email is required" )]
  10. public string  Email { get ; set ; }
  11.         [Required(ErrorMessage ="Password is required" )]
  12. public string  Password { get ; set ; }
  13.     }
  14. }

Create class "LoginModel" for user login.

LoginModel.cs

  1. using  System.ComponentModel.DataAnnotations;
  2. namespace  JWTAuthentication.Authentication
  3. {
  4. public class  LoginModel
  5.     {
  6.         [Required(ErrorMessage ="User Name is required" )]
  7. public string  Username { get ; set ; }
  8.         [Required(ErrorMessage ="Password is required" )]
  9. public string  Password { get ; set ; }
  10.     }
  11. }

We can create a class "Response" for returning the response value after user registration and user login. It will also return error messages, if the request fails.

Response.cs

  1. namespace  JWTAuthentication.Authentication
  2. {
  3. public class  Response
  4.     {
  5. public string  Status { get ; set ; }
  6. public string  Message { get ; set ; }
  7.     }
  8. }

We can create an API controller "AuthenticateController" inside the "Controllers" folder and add below code.

AuthenticateController.cs

  1. using  JWTAuthentication.Authentication;
  2. using  Microsoft.AspNetCore.Http;
  3. using  Microsoft.AspNetCore.Identity;
  4. using  Microsoft.AspNetCore.Mvc;
  5. using  Microsoft.Extensions.Configuration;
  6. using  Microsoft.IdentityModel.Tokens;
  7. using  System;
  8. using  System.Collections.Generic;
  9. using  System.IdentityModel.Tokens.Jwt;
  10. using  System.Security.Claims;
  11. using  System.Text;
  12. using  System.Threading.Tasks;
  13. namespace  JWTAuthentication.Controllers
  14. {
  15.     [Route("api/[controller]" )]
  16.     [ApiController]
  17. public class  AuthenticateController : ControllerBase
  18.     {
  19. private readonly  UserManager<ApplicationUser> userManager;
  20. private readonly  RoleManager<IdentityRole> roleManager;
  21. private readonly  IConfiguration _configuration;
  22. public  AuthenticateController(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IConfiguration configuration)
  23.         {
  24. this .userManager = userManager;
  25. this .roleManager = roleManager;
  26.             _configuration = configuration;
  27.         }
  28.         [HttpPost]
  29.         [Route("login" )]
  30. public  async Task<IActionResult> Login([FromBody] LoginModel model)
  31.         {
  32.             var user = await userManager.FindByNameAsync(model.Username);
  33. if  (user != null  && await userManager.CheckPasswordAsync(user, model.Password))
  34.             {
  35.                 var userRoles = await userManager.GetRolesAsync(user);
  36.                 var authClaims =new  List<Claim>
  37.                 {
  38. new  Claim(ClaimTypes.Name, user.UserName),
  39. new  Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
  40.                 };
  41. foreach  (var userRole in  userRoles)
  42.                 {
  43.                     authClaims.Add(new  Claim(ClaimTypes.Role, userRole));
  44.                 }
  45.                 var authSigningKey =new  SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration[ "JWT:Secret" ]));
  46.                 var token =new  JwtSecurityToken(
  47.                     issuer: _configuration["JWT:ValidIssuer" ],
  48.                     audience: _configuration["JWT:ValidAudience" ],
  49.                     expires: DateTime.Now.AddHours(3),
  50.                     claims: authClaims,
  51.                     signingCredentials:new  SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
  52.                     );
  53. return  Ok( new
  54.                 {
  55.                     token =new  JwtSecurityTokenHandler().WriteToken(token),
  56.                     expiration = token.ValidTo
  57.                 });
  58.             }
  59. return  Unauthorized();
  60.         }
  61.         [HttpPost]
  62.         [Route("register" )]
  63. public  async Task<IActionResult> Register([FromBody] RegisterModel model)
  64.         {
  65.             var userExists = await userManager.FindByNameAsync(model.Username);
  66. if  (userExists != null )
  67. return  StatusCode(StatusCodes.Status500InternalServerError, new  Response { Status = "Error" , Message = "User already exists!"  });
  68.             ApplicationUser user =new  ApplicationUser()
  69.             {
  70.                 Email = model.Email,
  71.                 SecurityStamp = Guid.NewGuid().ToString(),
  72.                 UserName = model.Username
  73.             };
  74.             var result = await userManager.CreateAsync(user, model.Password);
  75. if  (!result.Succeeded)
  76. return  StatusCode(StatusCodes.Status500InternalServerError, new  Response { Status = "Error" , Message = "User creation failed! Please check user details and try again."  });
  77. return  Ok( new  Response { Status = "Success" , Message = "User created successfully!"  });
  78.         }
  79.         [HttpPost]
  80.         [Route("register-admin" )]
  81. public  async Task<IActionResult> RegisterAdmin([FromBody] RegisterModel model)
  82.         {
  83.             var userExists = await userManager.FindByNameAsync(model.Username);
  84. if  (userExists != null )
  85. return  StatusCode(StatusCodes.Status500InternalServerError, new  Response { Status = "Error" , Message = "User already exists!"  });
  86.             ApplicationUser user =new  ApplicationUser()
  87.             {
  88.                 Email = model.Email,
  89.                 SecurityStamp = Guid.NewGuid().ToString(),
  90.                 UserName = model.Username
  91.             };
  92.             var result = await userManager.CreateAsync(user, model.Password);
  93. if  (!result.Succeeded)
  94. return  StatusCode(StatusCodes.Status500InternalServerError, new  Response { Status = "Error" , Message = "User creation failed! Please check user details and try again."  });
  95. if  (!await roleManager.RoleExistsAsync(UserRoles.Admin))
  96.                 await roleManager.CreateAsync(new  IdentityRole(UserRoles.Admin));
  97. if  (!await roleManager.RoleExistsAsync(UserRoles.User))
  98.                 await roleManager.CreateAsync(new  IdentityRole(UserRoles.User));
  99. if  (await roleManager.RoleExistsAsync(UserRoles.Admin))
  100.             {
  101.                 await userManager.AddToRoleAsync(user, UserRoles.Admin);
  102.             }
  103. return  Ok( new  Response { Status = "Success" , Message = "User created successfully!"  });
  104.         }
  105.     }
  106. }

We have added three methods "login", "register", and "register-admin" inside the controller class. Register and register-admin are almost same but the register-admin method will be used to create a user with admin role. In login method, we have returned a JWT token after successful login.

We can make below changes in "ConfigureServices" and "Configure" methods in "Startup" class as well.

Startup.cs

  1. using  JWTAuthentication.Authentication;
  2. using  Microsoft.AspNetCore.Authentication.JwtBearer;
  3. using  Microsoft.AspNetCore.Builder;
  4. using  Microsoft.AspNetCore.Hosting;
  5. using  Microsoft.AspNetCore.Identity;
  6. using  Microsoft.EntityFrameworkCore;
  7. using  Microsoft.Extensions.Configuration;
  8. using  Microsoft.Extensions.DependencyInjection;
  9. using  Microsoft.Extensions.Hosting;
  10. using  Microsoft.IdentityModel.Tokens;
  11. using  System.Text;
  12. namespace  JWTAuthentication
  13. {
  14. public class  Startup
  15.     {
  16. public  Startup(IConfiguration configuration)
  17.         {
  18.             Configuration = configuration;
  19.         }
  20. public  IConfiguration Configuration { get ; }
  21. public void  ConfigureServices(IServiceCollection services)
  22.         {
  23.             services.AddControllers();
  24.             services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ConnStr" )));
  25.             services.AddIdentity<ApplicationUser, IdentityRole>()
  26.                 .AddEntityFrameworkStores<ApplicationDbContext>()
  27.                 .AddDefaultTokenProviders();
  28.             services.AddAuthentication(options =>
  29.             {
  30.                 options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
  31.                 options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
  32.                 options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
  33.             })
  34.             .AddJwtBearer(options =>
  35.             {
  36.                 options.SaveToken =true ;
  37.                 options.RequireHttpsMetadata =false ;
  38.                 options.TokenValidationParameters =new  TokenValidationParameters()
  39.                 {
  40.                     ValidateIssuer =true ,
  41.                     ValidateAudience =true ,
  42.                     ValidAudience = Configuration["JWT:ValidAudience" ],
  43.                     ValidIssuer = Configuration["JWT:ValidIssuer" ],
  44.                     IssuerSigningKey =new  SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration[ "JWT:Secret" ]))
  45.                 };
  46.             });
  47.         }
  48. public void  Configure(IApplicationBuilder app, IWebHostEnvironment env)
  49.         {
  50. if  (env.IsDevelopment())
  51.             {
  52.                 app.UseDeveloperExceptionPage();
  53.             }
  54.             app.UseRouting();
  55.             app.UseAuthentication();
  56.             app.UseAuthorization();
  57.             app.UseEndpoints(endpoints =>
  58.             {
  59.                 endpoints.MapControllers();
  60.             });
  61.         }
  62.     }
  63. }

We can add "Authorize" attribute inside the "WeatherForecast" controller.

We must create a database and required tables before running the application. As we are using entity framework, we can use below database migration command with package manger console to create a migration script.

"add-migration Initial"

Use below command to create database and tables.

"update-database"

If you check the database using SQL server object explorer, you can see that below tables are created inside the database.

Above seven tables are used by identity framework to manage authentication and authorization.

We can run the application and try to access get method in weatherforecast controller from Postman tool.

We have received a 401 unauthorized error. Because, we have added Authorize attribute to entire controller. We must provide a valid token via request header to access this controller and methods inside the controller.

We can create a new user using register method in authenticate controller.

We can use above user credentials to login and get a valid JWT token.

We have received a token after successful login with above credentials.

We can pass above token value as a bearer token inside the authorization tab and call get method of weatherforecast controller again.

This time, we have successfully received the values from controller.

We can modify the weatherforecast controller with role-based authorization.

Now, only users with admin role can access this controller and methods.

We can try to access the weatherforecast controller with same token again in Postman tool.

We have received a 403 forbidden error now. Even though, we are passing a valid token we don't have sufficient privilege to access the controller. To access this controller, user must have an admin role permission. Current user is a normal user and do not have any admin role permission.

We can create a new user with admin role. We already have a method "register-admin" in authenticate controller for the same purpose.

We can login with this new user credentials and get a new token and use this token instead of old token to access the weatherforecast controller.

We have again received the values from weatherforecast controller successfully.

We can see the token payload and other details using jwt.io site.

Inside the payload section, you can see the user name, role and other details as claims.


Conclusion

In this post, we have seen how to create a JSON web token in ASP.NET Core Web API application and use this token for authentication and authorization. We have created two users, one without any role and one with admin role. We have applied the authentication and authorization in controller level and saw the different behaviors with these two users.

How To Create Json Webservice In Asp Net

Source: https://www.c-sharpcorner.com/article/authentication-and-authorization-in-asp-net-core-web-api-with-json-web-tokens/

Posted by: hubbardandome.blogspot.com

0 Response to "How To Create Json Webservice In Asp Net"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel