Token can't store in .NET Core

Viewed 173

I have a project using .NET core version 3.1 and I'm using token for logging in. Everything works perfectly when testing with Postman, it created token and I can use it to access the Home page.

The problem is, when I started testing on client side, it doesn't work. I debugged and saw after logging in, the token is generated but I can't access the HomeController because of [Authorize] attribute.

This is my code to generate token:

public async Task<HttpResponse<LoginResult>> GetTokenAsync(LoginRequest loginInfo)
{
    var audience = await _audiences.FindAsync(a => a.Id == loginInfo.ClientId);
    string message = string.Empty;

    if (audience != null)
    {
        bool audienceIsValid = _jwtProvider.ValidateAudience(audience.Issuer
                    , audience.SecretKey
                    , ref message);

        if (audienceIsValid)
            return await GenerateToken(loginInfo);
        else
            message = ErrorMessages.Login_AudienceInvalid;
    }
    else
        message = string.Format(ErrorMessages.Login_Not_Permitted, "Your client Id");
    return HttpResponse<LoginResult>.Error(message, HttpStatusCode.BadRequest);
}

I guess that token couldn't be stored correctly.

What am I missing?

UPDATE This is my code in login

[HttpPost]
[Route("login")]
[AllowAnonymous]
public async Task<ActionResult> Login([FromForm]LoginRequest model)
{
      model.ClientId = 1;
      var response = await _services.GetTokenAsync(model);
      if (response.StatusCode == 200)
      {
           return RedirectToAction("Index", "Home");
      }
      return RedirectToAction("Login");
}

And this is what I'm trying to access

[HttpGet]
[Route("index")]
[Authorize]
public IActionResult Index()
{
     return View();
}
1 Answers

You need to create a custom policy to specify in the Authorize attribute that is configured to use a custom requirement handler

First you lay out the requirement of the custom policy via a class that inherits IAuthorizationRequirement

public class TokenRequirement : IAuthorizationRequirement
{
}

This is where you would optionally accept parameters if you need them. But normally you pass a token in the header of a request which your custom policy's requirement handler would have access to without the need for explicit parameters.

Your requirement's requirement handler to be used by your custom policy would look something like this

public class TokenHandler : AuthorizationHandler<TokenRequirement>
{
    //Some kind of token validator logic injected into your handler via DI
    private readonly TokenValidator _tokenValidator;

    //The http context of this request session also injected via DI
    private readonly IHttpContextAccessor _httpCtx;

    //The name of the header your token can be found under on a Http Request
    private const string tokenHeaderKey = "authToken";

    //Constructor using DI to get a instance of a TokenValidator class you would
    //have written yourself, and the httpContext
    public TokenHandler(TokenValidator tokenValidator, IHttpContextAccessor httpCtx)
    {
        _tokenValidator = tokenValidator;
        _httpCtx = httpCtx;
    }

    //Overriden implementation of base class AuthorizationHandler's HandleRequirementAsync method 
    //This is where you check your token.
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context
        ,TokenRequirement requirement)
    {
        if (context.Resource is Endpoint endpoint)
        {
            HttpRequest httpReqCtx = _httpCtx.HttpContext.Request;
            string token =
                httpReqCtx.Headers.TryGetValue(tokenHeaderKey, out StringValues tokenVal)
                    ? tokenVal.FirstOrDefault()
                    : null;

            if (string.IsNullOrWhitespace(token))
            {
                context.Fail();
            }
            else
            {
                bool tokenIsValid = await _tokenValidator.ValidToken(token);
                if(tokenIsValid)
                    context.Succeed(requirement);
                else
                    context.Fail();
            }
        }
        return Task.CompletedTask;
    }
}

You'd register your custom requirement handler on a custom policy name in Startup.cs like so

//This is a framework extension method under Microsoft.Extensions.DependencyInjection
services.AddHttpContextAccessor();

//Your custom handler
services.AddSingleton<IAuthorizationHandler, TokenHandler>();

//Your custom policy
services.AddAuthorization(options =>
{
    options.AddPolicy(
        //Your custom policy's name, can be whatever you want
        "myCustomTokenCheckerPolicy",
        //The requirement your policy is going to check
        //Which will be handled by the req handler added above
        policy => policy.Requirements.Add(new TokenRequirement())
    );
});

The impl on the attribute would look like this

[HttpGet]
[Route("index")]
[Authorize(Policy = "myCustomTokenCheckerPolicy")]
public IActionResult Index()
{
    return View();
}
Related