0
votes

I'm trying to setup my authorization policy with a jwt based token for my apis. I have two controllers, used by separate apis. I need to make sure a user can only access the ones that he/she is allowed to use. So I figured I'd go with policy based authorization

    [Authorize(Policy = "API1")]
    [Route("api1/endpoint")]
    public class API1Controller : Controller
    {
           // my actions for api 1
     }
    [Authorize(Policy = "API2")]
    [Route("api2/endpoint")]
    public class API2Controller : Controller
    {
           // my actions for api 2
    }

Adding policies on Startup

    services.AddAuthorization(options => {
                    options.AddPolicy("API1User", policy => policy.Requirements.Add(new ApplicationTypeRequirement(ApplicationType.API1)));
                    options.AddPolicy("API2User", policy => policy.Requirements.Add(new ApplicationTypeRequirement(ApplicationType.API2)));
                });
//  Adding handlers after this

So my question is, where is the best place to call a stored procedure to check the database for the users application permission. Reading from the following, (https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.1) , it details the use of the claims from the token.

Right now what I have with the JWT token that I save is the userid, first name, last name and email, that's it.

1

1 Answers

0
votes

The best place to check authentication and authorization in ActionFilter, you can check auth policy in database side and also with JWT.

If you want to authorise your controller, you have to use a middle ware (ActionFilterAttribute), which will detect user's http request and validate them by decoding user's token. you can filter all http methods (GET,POST,PUT,DELETE...etc), and can implement your own authorisation logic for specific http method.

AuthorizationRequiredAttribute.cs

N.B: here all codes are not relevant to your problem. but hope you'll understand how actually i filter get/post request with condition.

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class AuthorizationRequiredAttribute : ActionFilterAttribute
{
    private readonly IAccessTokenServices _accessTokenServices;
    private readonly IPermissionServices _permissionServices;
    private readonly IAuditLogServices _auditLogServices;
    private IConfiguration _config;
    public AuthorizationRequiredAttribute(IAccessTokenServices accessTokenServices, IPermissionServices permissionServices,
        IAuditLogServices auditLogServices,IConfiguration config)
    {
        _accessTokenServices = accessTokenServices;
        _config = config;
        _permissionServices = permissionServices;
        _auditLogServices = auditLogServices;
    }
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        try
        {
            if (context.HttpContext.Request.Headers.ContainsKey(Constants.HttpHeaders.Token))
            {
                var handler = new JwtSecurityTokenHandler();
                var token = handler.ReadToken(context.HttpContext.Request.Headers[Constants.HttpHeaders.Token])
                    as JwtSecurityToken;
                var expireDate = Convert.ToDateTime(token.Claims.First(claim => claim.Type == Constants.JwtClaims.ExpiresOn).Value);
                if (context.HttpContext.Request.Method == WebRequestMethods.Http.Get)
                {
                    if (expireDate < DateTime.Now)
                    {
                        context.Result = new UnauthorizedResult();
                    }
                }
                else
                {

                    var accessToken = _accessTokenServices
                        .Details(x => x.Token == context.HttpContext.Request.Headers[Constants.HttpHeaders.Token]);
                    if (accessToken != null)
                    {
                        if (accessToken.ExpiresOn < DateTime.Now)
                        {
                            _accessTokenServices.Delete(accessToken);
                            context.Result = new UnauthorizedResult();
                        }
                        else
                        {
                            var userId = Convert.ToInt32(token.Claims.First(claim => claim.Type == Constants.JwtClaims.UserId).Value);
                            var userTypeId = Convert.ToInt32(token.Claims.First(claim => claim.Type == Constants.JwtClaims.UserTypeId).Value);
                            if (accessToken == null)
                            {
                                context.Result = new UnauthorizedResult();
                            }
                            else if (!_permissionServices.IsPermissionExist(context.HttpContext.Request.Path.ToString(), userTypeId))
                            {
                                context.Result = new StatusCodeResult((int)HttpStatusCode.NotAcceptable);
                            }
                            else
                            {

                                _auditLogServices.Save(context.HttpContext.Request.Path.ToString(), userId);
                                accessToken.ExpiresOn = DateTime.Now.AddMinutes(Convert.ToInt16(_config["Jwt:ExpiresOn"]));
                                _accessTokenServices.UpdateExpireTime(accessToken);

                            }
                        }
                    }
                    else
                    {
                        context.Result = new UnauthorizedResult();
                    }
                }
            }
            else
            {
                context.Result = new NotFoundResult();
            }
        }
        catch (Exception ex)
        {
            context.Result = new BadRequestResult();
        }
        base.OnActionExecuting(context);
    }
}

}

HomeController.cs

Now you can use AuthorizationRequiredAttribute as api/controller filter service. i have modified your controller and see the Message method

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

namespace Chat.Controllers
{
    [Route("api/home")]
    public class HomeController : Controller
    {

        public IActionResult Index()
        {
            return View();
        }

        [HttpGet("message"), ServiceFilter(typeof(AuthorizationRequiredAttribute))]
        public IActionResult Message()
        {
            return Ok("Hello World!");
        }
    }
}