alper
alper

Reputation: 153

Custom Authorization Attribute Fails To Work When Entered JWT Token in Request Header

[a simple Web API in ASP.NET]

The code gives 401 Unauthorized response when I send a request without JWT token. everything looks good up to this point.

When I enter the JWT token which is generated by the generateJWT() method in my code to authenticate a user the request ends without reaching the custom authorization attribute and the controller.

this is the custom authorization attribute:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeAttribute : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = (User)context.HttpContext.Items["User"];

        if (user == null)
        {
            context.Result = new JsonResult(new { message = "unauthorized" }) { StatusCode = StatusCodes.Status401Unauthorized };
        }
    }
}

when I don't add a jwt token into the request header, the request reaches here and gives the 401 response.

With jwt token, the request does not reach here. It ends in the custom JwtMiddleware although it works perfectly as far as I obsevered, validates the token and finds the user:

public class JwtMiddleware
{
    private readonly RequestDelegate _next;
    private readonly AppSettings _appSettings;

    public JwtMiddleware(RequestDelegate next, IOptions<AppSettings> appSettings)
    {
        _next = next;
        _appSettings = appSettings.Value;
    }

    public async Task Invoke(HttpContext context, IUserService userService)
    {
        var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();

        if (token == null) await _next(context);

        attachUserToContext(context, userService, token);
    }

    private void attachUserToContext(HttpContext context, IUserService userService, string token)
    {
        try
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(_appSettings.Secret);

            tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
                ClockSkew = TimeSpan.Zero
            }, out SecurityToken validatedToken);

            var jwtToken = (JwtSecurityToken)validatedToken;
            var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value);

            context.Items["User"] = userService.GetById(userId);
        }

        catch
        {
            // do nothing
        }


    }
}

Can you spot what's going on here?

Upvotes: 0

Views: 445

Answers (1)

ochzhen
ochzhen

Reputation: 156

In the middleware, looks like the RequestDelegate is invoked only if there's no token provided: if (token == null) await _next(context);

And if there's a token, attachUserToContext method is run but _next(context) is not invoked, thus the rest of the pipeline is not being executed (this is where mvc pipeline is along with the custom attribute).

From the docs: Each middleware component in the request pipeline is responsible for invoking the next component in the pipeline or short-circuiting the pipeline.

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1

So, we should change the logic to always invoke _next:

var token = ...

if (token != null)
    attachUserToContext(context, userService, token);

await _next(context);

Another possible option is to short-circuit the pipeline if no token provided by writing 401 status code to context.Response and not invoking _next.

Upvotes: 1

Related Questions