且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

在.NET Core中使用JWT令牌或API密钥的授权机制

更新时间:2023-02-15 08:28:25

I was trying to create an API-key attribute in a way shown here and decorate all controllers with it but, this was only working when no [Authorize] attribute was set. When I set [Authorize] attribute and set a breakpoint in my API-key attribute it will never be reached and I'm getting 401 Unauthorized as a response.

The issue relates that when using custom API-Key attribute with the [Authorize] attribute together, the controller or the action method will be configured both JWT authentication and API-Key validation, in the request header, it should contain both the JWT token and API-Key. If any of them are missing, it will display a 401 Unauthorized error.

Is there any chance to have both authorization methods working in parallel? If so, please guide me on how to achieve that.

Do you mean use any of the authorization method, you could access the related action method, if that is the case, you could try to use the custom the Authorization attributes.

For example: create a CustomAuthorization with the following code:

[AttributeUsage(AttributeTargets.Class)]
public class CustomAuthorization : Attribute, IAuthorizationFilter
{ 
    /// <summary>  
    /// This will Authorize User  
    /// </summary>  
    /// <returns></returns>  
    public void OnAuthorization(AuthorizationFilterContext filterContext)
    {

        if (filterContext != null)
        {
            //get the authorization header
            Microsoft.Extensions.Primitives.StringValues authTokens;
            filterContext.HttpContext.Request.Headers.TryGetValue("Authorization", out authTokens);

            var _token = authTokens.FirstOrDefault();

            if (_token != null)
            {
                string authToken = _token;
                if (authToken != null)
                {
                    if (IsValidToken(authToken))
                    {
                        filterContext.HttpContext.Response.Headers.Add("Authorization", authToken);
                        filterContext.HttpContext.Response.Headers.Add("AuthStatus", "Authorized");

                        filterContext.HttpContext.Response.Headers.Add("storeAccessiblity", "Authorized");

                        return;
                    }
                    else
                    {
                        filterContext.HttpContext.Response.Headers.Add("Authorization", authToken);
                        filterContext.HttpContext.Response.Headers.Add("AuthStatus", "NotAuthorized");

                        filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                        filterContext.HttpContext.Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "Not Authorized";
                        filterContext.Result = new JsonResult("NotAuthorized")
                        {
                            Value = new
                            {
                                Status = "Error",
                                Message = "Invalid Token"
                            },
                        };
                    }

                }

            }
            else
            {
                //if the request header doesn't contain the authorization header, try to get the API-Key.
                Microsoft.Extensions.Primitives.StringValues apikey;
                var key = filterContext.HttpContext.Request.Headers.TryGetValue("ApiKey", out apikey);
                var keyvalue = apikey.FirstOrDefault();

                //if the API-Key value is not null. validate the API-Key.
                if(keyvalue != null)
                {
                    filterContext.HttpContext.Response.Headers.Add("ApiKey", keyvalue);
                    filterContext.HttpContext.Response.Headers.Add("AuthStatus", "Authorized");

                    filterContext.HttpContext.Response.Headers.Add("storeAccessiblity", "Authorized");

                    return;
                }

                
                filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                filterContext.HttpContext.Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "Please Provide authToken";
                filterContext.Result = new JsonResult("Please Provide auth Token")
                {
                    Value = new
                    {
                        Status = "Error",
                        Message = "Please Provide auth Token"
                    },
                };  
            }
        }
    }

    public bool IsValidToken(string authToken)
    {
        //validate Token here  
        return true;
    }
}

Then, use the above CustomAuthorization in the API controller:

[Route("api/[controller]")]
[ApiController]
//[Authorize]
//[ApiKey]
[CustomAuthorization]
public class ValuesController : ControllerBase
{
    // GET: api/<ValuesController>
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

The result like this:

Refer this article.

Besides, you could also consider group the action method based on the Authorization method, for the API-key related action method or controller, only add the custom API-Key attribute, for the action method needs to JWT authentication, just add the [Authorize] attribute.