atfergs
atfergs

Reputation: 1684

.NET Core 2 Web API JWT token not recognized

I followed this tutorial to configure JWT authorization in my Web API app. The token generation and handout works fine, but when I send a request back to the server with the token, it doesn't populate the identity, so it fails if authorization is required.

I've tested both with a reactjs frontend and Postman. Both end up returning nothing (without Authorize decorator - User.Identity.isAuthorized is false), or 404 with the decorator. I have confirmed that the token is being sent properly.

I'm also using Identity, if that matters.

ConfigureServices method

    public void ConfigureServices(IServiceCollection services)
    {
        ...

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = Configuration["Jwt:Issuer"],
                ValidAudience = Configuration["Jwt:Audience"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
            };
        });
   }

Configure method

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseAuthentication();

        app.UseCors("SiteCorsPolicy");

        app.UseMvc();

        ...
    }

Function to build the token

    private string BuildToken(AuthViewModel user)
    {
        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, user.Username),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken
        (
            _config["Jwt:Issuer"],
            _config["Jwt:Audience"],
            //claims,
            expires: DateTime.Now.AddMinutes(30),
            signingCredentials: creds
        );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

Excerpt from appsettings.json

"Jwt": {
    "Key": "<secret stuff>",
    "Issuer": "http://localhost:53530/",
    "Audience": "http://localhost:8080/"
 }

Test function I'm trying to call but is failing

    [HttpGet("currentuser"), Authorize]
    public async Task<ApplicationUser> GetCurrentUser()
    {
        var username = User.Identity.Name;

        return await _context.ApplicationUsers.SingleOrDefaultAsync(u => u.UserName == username);
    }

Upvotes: 0

Views: 1590

Answers (3)

Alexandru Mitu
Alexandru Mitu

Reputation: 31

If you dont't want to declare an authorization policy you should:

  1. In "Program.cs" or "Startup.cs" depending the version you're working on, you declare at the bottom:
app.UseAuthentication();

app.UseAuthorization();

You need to declare them in this order!!!

  1. Make sure you declare an authentication scheme:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options => {...})

If you don't declare the authorization scheme, it will automatically use the scheme declared in the "AddAuthentication()" method.

Without the authorization policy, the app may not know which authentication scheme to use to authenticate the user and may not be able to validate the JWT token.

So, it is important to have both AddAuthentication and AddAuthorization configured properly in your app to ensure proper authentication and authorization of users.

Upvotes: 2

atfergs
atfergs

Reputation: 1684

I figured it out. I had to add a new Authorization Policy.

services.AddAuthorization(auth =>
{
    auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
        .RequireAuthenticatedUser().Build());
});

Then I decorated the controller with

[Authorize("Bearer"]

I've been messing with this for a couple days, trying different tutorials, so I know this was working at one point without the policy. Dunno why I needed it this time or why it wasn't part of the tutorial.

If someone figures out what I screwed up in the first place, I'm all ears.

Upvotes: 2

Norbert Korny
Norbert Korny

Reputation: 958

I ran into the same issue (.net core 2.1) and was really happy to make it work using your answer @atfergs.

After fiddling with the whole setup I found out that no new Authorization Policy is required.

It is sufficient to decorate the controller with

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

considering the following setup

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(options =>
        {...}

Now

    User?.Identity?.IsAuthenticated

is true :)

Cheers!

Upvotes: 1

Related Questions