question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Jwt token authorization not working

See original GitHub issue

Describe the bug Authorize directive not working with JWT

To Reproduce Steps to reproduce the behavior:

public void ConfigureServices(IServiceCollection services)
{
      var jwtSettings = new JwtSettings();
      Configuration.Bind(nameof(JwtSettings), jwtSettings);
      services.AddSingleton<IJwtSettings>(jwtSettings);

      services.AddIdentity<NZUser, NZRole>()
        .AddEntityFrameworkStores<NomadZeteticDbContext>()
        .AddDefaultUI()
        .AddDefaultTokenProviders();

      var key = Encoding.ASCII.GetBytes(jwtSettings.Secret);
      services.AddAuthentication(x =>
      {
        x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
      })
      .AddJwtBearer(x =>
      {
        x.RequireHttpsMetadata = false;
        x.SaveToken = true;
        x.TokenValidationParameters = new TokenValidationParameters
        {
          ValidateIssuerSigningKey = true,
          IssuerSigningKey = new SymmetricSecurityKey(key),
          ValidateIssuer = false,
          ValidateAudience = false
        };
      });

      services.AddDbContext<NomadZeteticDbContext>(options =>
      {
        options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"), builder =>
          {
            builder.MigrationsAssembly("NomadZetetic.Data");
          });
      });

      services.Configure<IdentityOptions>(options =>
      {
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;
      });

      services.AddScoped<IUserService, UserService>();
      services.AddGraphQL(GraphQL.Builder.BuildSchema);
      services.AddMvc();
}
public static class Builder
{
    public static ISchema BuildSchema(IServiceProvider serviceProvider)
    {
      return SchemaBuilder.New()
        .AddServices(serviceProvider)
        .AddAuthorizeDirectiveType()
        .AddType<UserType>()
        .AddType<SignInResponseType>()
        .AddQueryType<QueryType>()
        .Create();
    }
}
public class UserService : IUserService
{
    private readonly IJwtSettings jwtSettings;
    private readonly NomadZeteticDbContext dbContext;

    public UserService(NomadZeteticDbContext dbContext, IJwtSettings jwtSettings)
    {
      this.dbContext = dbContext;
      this.jwtSettings = jwtSettings;
    }

    public async Task<ISignInResponse> SignIn(string email, string password)
    {
      var passwordHasher = new PasswordHasher<NZUser>();
      var user = await dbContext.Users.SingleOrDefaultAsync(q => q.Email == email).ConfigureAwait(false);
      if (user == null)
      {
        throw new AuthenticationException("User not found");
      }

      var passwordVerificationResult = passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password);
      if (passwordVerificationResult != PasswordVerificationResult.Success)
      {
        throw new AuthenticationException("Invalid password");
      }

      var tokenHandler = new JwtSecurityTokenHandler();
      var key = Encoding.ASCII.GetBytes(jwtSettings.Secret);
      var tokenDescriptor = new SecurityTokenDescriptor
      {
        Issuer = jwtSettings.Issuer,
        Audience = jwtSettings.Audience,
        IssuedAt = DateTime.Now,
        Subject = new ClaimsIdentity(new Claim[]
        {
          new Claim(ClaimTypes.Name, user.Id.ToString())
        }),
        Expires = DateTime.UtcNow.AddDays(7),
        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
      };
      var token = tokenHandler.CreateToken(tokenDescriptor);
      var accessToken = tokenHandler.WriteToken(token);
      return new SignInReponse { AccessToken = accessToken };
    }

    public async Task<INZUser> GetUserById(string userId)
    {
      return await dbContext.Users.SingleAsync(u => u.Id == Guid.Parse(userId));
    }
}
public class Query
{
    private readonly IUserService userService;

    public Query(IUserService userService)
    {
      this.userService = userService;
    }

    public async Task<ISignInResponse> SignIn(string email, string password)
    {
      return await userService.SignIn(email, password);
    }

    public async Task<INZUser> Me()
    {
      return await userService.GetUserById("aa"); // this just for test - i want to see an error
    }
}

public class QueryType : ObjectType<Query>
{
    protected override void Configure(IObjectTypeDescriptor<Query> descriptor)
    {
      descriptor.Field(t => t.SignIn(default, default))
        .Argument("email", a => a.Type<NonNullType<StringType>>())
        .Argument("password", a => a.Type<NonNullType<StringType>>())
        .Type<NonNullType<SignInResponseType>>();

      descriptor.Field(t => t.Me())
        .Authorize()
        .Type<NonNullType<UserType>>();
    }
}

public class SignInResponseType : ObjectType<ISignInReponse>
{
    protected override void Configure(IObjectTypeDescriptor<ISignInReponse> descriptor)
    {
      descriptor.Field(t => t.AccessToken)
        .Type<NonNullType<StringType>>();
    }
}

public class UserType : ObjectType<INZUser>
{
    protected override void Configure(IObjectTypeDescriptor<INZUser> descriptor)
    {
      descriptor.Name("User");
      descriptor.Field(t => t.Id).Type<NonNullType<StringType>>();
      descriptor.Field(t => t.UserName).Type<NonNullType<StringType>>();
    }
}

image

That’s what i got.

All working with classical [Authorize] attribute for built in classical controllers. But for graphql it’s not working. Maybe exist other way to authorize user with JWT token via middleware (if yes could somebody give an example how to do this)?

Expected behavior

Should throw an error…

Desktop (please complete the following information):

  • OS: MacOS (asp.net core 2.2)
  • Version Latest

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
michaelstaibcommented, Aug 16, 2019

After looking at you repro I saw that the middleware order was not correct:

  app
        .UseCors(x => x
          .AllowAnyOrigin()
          .AllowAnyMethod()
          .AllowAnyHeader()
        )
        .UseWebSockets()
        .UseGraphQL(new QueryMiddlewareOptions
        {
          Path = "/graphql",
          EnableSubscriptions = false
        })
        .UseHttpsRedirection()
        .UseAuthentication()
        .UseMvc();

The middleware is like a pipeline where the request is handled by each middleware. Since authentication is coming after GraphQL the authentication middleware is never able to handle the request.

A correct order would look like the following:

  app
       .UseCors(x => x
          .AllowAnyOrigin()
          .AllowAnyMethod()
          .AllowAnyHeader()
        )
        .UseHttpsRedirection()
        .UseAuthentication()        
        .UseWebSockets()
        .UseGraphQL(new QueryMiddlewareOptions
        {
          Path = "/graphql",
          EnableSubscriptions = false
        })
        .UseMvc();
1reaction
chungonioncommented, Jun 18, 2021

After looking at you repro I saw that the middleware order was not correct:

  app
        .UseCors(x => x
          .AllowAnyOrigin()
          .AllowAnyMethod()
          .AllowAnyHeader()
        )
        .UseWebSockets()
        .UseGraphQL(new QueryMiddlewareOptions
        {
          Path = "/graphql",
          EnableSubscriptions = false
        })
        .UseHttpsRedirection()
        .UseAuthentication()
        .UseMvc();

The middleware is like a pipeline where the request is handled by each middleware. Since authentication is coming after GraphQL the authentication middleware is never able to handle the request.

A correct order would look like the following:

  app
       .UseCors(x => x
          .AllowAnyOrigin()
          .AllowAnyMethod()
          .AllowAnyHeader()
        )
        .UseHttpsRedirection()
        .UseAuthentication()        
        .UseWebSockets()
        .UseGraphQL(new QueryMiddlewareOptions
        {
          Path = "/graphql",
          EnableSubscriptions = false
        })
        .UseMvc();

Thanks men, I faced a similar situation and your answer reminds me that I forget to add UseAuthentication in app . Cheers men, you saved my day =]

Read more comments on GitHub >

github_iconTop Results From Across the Web

Jwt tokens authorization is not working
I ran into a similar issue when I setup JWT and kept getting 401s. Try changing the order in your startup class so...
Read more >
Troubleshooting JWT authentication
JWT tokens are NOT presented in an HTTP Authorization header. A JWT is not itself a way to authenticate to the API. Instead,...
Read more >
The auth is not working in .net core web api?
For example, if using JWT, ensure that the token is being passed in the "Authorization" header with the "Bearer " prefix. Check the ......
Read more >
ASP.NET Core Web API: Troubleshooting
1. Are you passing the JWT in the Authorization header? ... Check if you are passing the JWT as a Bearer token in...
Read more >
Troubleshooting JwtBearer authentication problems in ASP ...
This error is typically generated by the JwtBearer handler, and it means that the incoming token is not found, valid or accepted. 403...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found