Search code examples
c#restopenid-connect.net-7.0

When configuring JwtBearer or OpenIDConnect, Bearer error="invalid_token", c# .net 7


Could use a hand getting JwtBearer or OpenIdConnect working with a webapi.

Generally when configuring an app I look for OpenIdConnect settings and configure as expected. This works and I've got single signon working with several apps. I believe this should eliminate keycloak as a suspect variable.

With the code below I was getting a 302 and after much searching added in the options.Events which led to the current 401 error:

    < HTTP/2 401
    < date: Sun, 26 Feb 2023 16:29:54 GMT
    < content-length: 0
    < www-authenticate: Bearer error="invalid_token"
    < strict-transport-security: max-age=15724800; includeSubDomains

However, I can take the bearer token being used and copy it out to jwt.io and it shows as valid. Not sure why the webapi is struggling here.

When I test, I'm using the swagger, clicking on the authentication and copying in 'Bearer ' then Executing a method which has the '[Authorize]'.

After increasing log level I'm now also seeing:

info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[7]
      Bearer was not authenticated. Failure message: No SecurityTokenValidator available for token.
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12]
      AuthenticationScheme: Bearer was challenged.

I've updated packages. Here's the code snippet:

Startup.cs

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.MetadataAddress = Configuration.GetSection("appSettings")["auth0Domain"] + "/.well-known/openid-configuration";
        options.Audience = Configuration.GetSection("appSettings")["auth0Audience"];
        options.RequireHttpsMetadata = false;
        options.IncludeErrorDetails = true;
        options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
        {
            ValidateIssuer = false,
            ValidateAudience = false
        };
        //add more configs
        options.Events = new JwtBearerEvents
        {
            OnMessageReceived = context =>
            {
                context.Token = context.Request.Headers["Authorization"];
                return Task.CompletedTask;
            },
        };
    });

    IdentityModelEventSource.ShowPII = true;


    // Configure Cors
    services.AddCors(options =>
    {
        options.AddDefaultPolicy(
            builder =>
            {
                builder.WithOrigins()
                        .AllowAnyOrigin()
                        .AllowAnyMethod()
                        .AllowAnyHeader();
                //builder.WithOrigins("http://example.com",
                //                    "http://www.contoso.com");
            });
    });
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, Service service)
        {
            Globals.service = service;
            service.Start();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            // Enable middleware to serve generated Swagger as a JSON endpoint.
            app.UseSwagger();

            // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), 
            // specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
            });

            //app.UseHttpsRedirection();

            app.UseRouting();

            // Allow cross-site calls
            app.UseCors();

            app.UseAuthentication();
            app.UseAuthorization();
            //app.UseMiddleware<ApiKeyMiddleware>();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

            app.UseWebSockets();

            // Handle incoming webrequests in order to start WebSockets
            app.Use(async (context, next) => { await Globals.service.tm.HandleWebRequest(context, next); });
        }

Solution

  • You should not need to add the options.Events part, if you pass the token via the usual Authorization: bearer header.

    I recently blogged about troubleshooting JwtBearer problems here and I hope that can give you some ideas. See my comments for additional things to add to your question.