Navigation

Thursday 29 August 2019

ASP.NET Core 2.0/2.1/2.2 - JWT Authentication(Token Based)

In this blog, we'll go through a simple example of how to implement role-based authorization/access control in an ASP.NET Core API with C#.


Step 1: Create users controller that handles all routes/endpoints for the API that relate to users standard CRUD operations.



using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using WebAPIAuth.Helpers;
using WebAPIAuth.Model;
using WebAPIAuth.Service;
namespace WebAPIAuth.Controllers
{
    [Produces("application/json")]
    [Authorize]
    [Route("[controller]")]
    public class UsersController : Controller
    {
        private readonly AppSettings _appSettings;
        private IAuthorizationService _authorizationService;
        public UsersController(IOptions<AppSettings> appSettings, IAuthorizationService authorizationService)
        {
            _appSettings = appSettings.Value;
            _authorizationService = authorizationService;
        }

        [HttpGet]
        [AllowAnonymous]
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        [AllowAnonymous]
        [HttpPost("authUsers")]
        public Users AuthUsers([FromBody]Users _user)
        {
            if(_user == null)
            {
                return null;
            }
            UserService userObj = new UserService();
            Users user = userObj.GetValidUser(_user.Username, _user.Password);
            if (user == null)
                return null;
            AuthorizeService auth = new AuthorizeService();
            string _token = auth.Authenticate(_user.Username,_appSettings, Role.Admin);
            user.Token = _token;
            return user;
        }

        //Role base Authentication
        [Authorize(Roles = Role.Admin)]
        [HttpGet]
        [Route("GetAll")]
        public IActionResult GetAll()
        {
            UserService _users = new UserService();
            var users = _users.GetAll();
            return Ok(users);
        }
        [HttpGet("{id}")]
        public IActionResult GetById(int id)
        {
            UserService _users = new UserService();
            var user = _users.GetById(id);

            if (user == null)
            {
                return NotFound();
            }
            // only allow admins to access other user records
            var currentUserId = User.Identity.Name;
            if (user.Username != currentUserId && !User.IsInRole("Admin"))
            {
                return Forbid();
            }
            return Ok(user);
        }
    }
}


Step 2: The user class represents the data for a user in the application.



namespace WebAPIAuth.Model
{
    public class Users
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public string Role { get; set; }
        public string Token { get; set; }
    }
}



Step 3: The role class defines all the roles.



namespace WebAPIAuth.Model
{
    public class Role
    {
        public const string Admin = "Admin";
        public const string User = "User";
    }
}


Step 4: The AppSettings class contains properties defined in the appsettings.json file as shown in step 5. The secret property get value via an IOptions<AppSettings> appSettings object that is injected into the controller constructor as shown in step 1.



namespace WebAPIAuth.Helpers
{
    public class AppSettings
    {
        public string Secret { get; set; }
    }
}

Step 5: The "Secret" property is used by the api to sign and verify tokens for authentication.


{
  "AppSettings": {
    "Secret": "THIS IS USED TO SIGN AND VERIFY PROJECT TOKENS, BY VIJAY"
  },
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  }
}


Step 6: The user service contains all methods and logic that is called by the Users controller.



using System.Collections.Generic;
using System.Linq;
using WebAPIAuth.Model;

namespace WebAPIAuth.Service
{

    public class UserService
    {
        private List<Users> _users = new List<Users>
        {
            new Users { Id = 1,Name = "Admin", Username = "admin", Password = "admin", Role = Role.Admin },
            new Users { Id = 2, Name = "User", Username = "user", Password = "user", Role = Role.User }
        };
      
        public Users GetValidUser(string username, string password)
        {
            var user = _users.SingleOrDefault(x => x.Username == username && x.Password == password);
            return user;
        }
        public IEnumerable<Users> GetAll()
        {
            // return users without passwords
            return _users.Select(x => {
                x.Password = null;
                return x;
            });
        }

        public Users GetById(int id)
        {
            var user = _users.FirstOrDefault(x => x.Id == id);

            // return user without password
            if (user != null)
                user.Password = null;

            return user;
        }
    }
}



Step 7: The authorize service contains a method for authenticating user credentials and return a token



using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using WebAPIAuth.Helpers;

namespace WebAPIAuth.Service
{
    public interface IAuthorizeService
    {
        string Authenticate(string username, AppSettings _appSettings, string role);
    }
    public class AuthorizeService: IAuthorizeService
    {     
      
        public string Authenticate(string username, AppSettings _appSettings, string role)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[]
                {
                    new Claim(ClaimTypes.Name, username),
                    // this is optional, If you wants role base authentication then use it
                    new Claim(ClaimTypes.Role,role)
                }),
                Expires = DateTime.UtcNow.AddDays(7),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };
            var token = tokenHandler.CreateToken(tokenDescriptor);
            string _token = tokenHandler.WriteToken(token);
            return _token;
        }
    }
}



Step 8: The startup class configures the request pipeline of the application.


using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.IdentityModel.Tokens;
using System.IO;
using System.Text;
using WebAPIAuth.Helpers;
using WebAPIAuth.Service;

namespace WebAPIAuth
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }
       
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors();
            services.AddMvc();
            // configure strongly typed settings objects
            var appSettingsSection = Configuration.GetSection("AppSettings");
            services.Configure<AppSettings>(appSettingsSection);

            // configure jwt authentication
            var appSettings = appSettingsSection.Get<AppSettings>();
            var key = Encoding.ASCII.GetBytes(appSettings.Secret);
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(x =>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
            });
            services.AddScoped<IAuthorizeService, AuthorizeService>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            // global cors policy
            app.UseCors(x => x
                .AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader());

            app.UseAuthentication();

            app.UseMvc();
        }
    }
}




Step 9: Following are the screenshot of  'Postman' by which api tested.






No comments:

Post a Comment