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.

An Async `AddJwtBearer` Lambda does not Complete for First Call

See original GitHub issue

Describe the bug

The call pipeline does not await the AddJwtBearer lambda when it is called via async with an await in it.

See this stack overflow answer for more details: https://stackoverflow.com/a/61196214/16241

To Reproduce

  1. Create a new .Net Core Web Application
  2. Select that you are making an API from the wizard
  3. Add the Microsoft.AspNetCore.Authentication.JwtBearer NuGet
  4. In Startup.cs, add using Microsoft.AspNetCore.Authentication.JwtBearer; to the top of the file.
  5. In Startup.cs, add the following code to the ConfigureServices method before the call to Services.AddControllers();
services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(async options =>
    {
        await Task.Delay(500);
        options.Events = new JwtBearerEvents()
        {
            OnMessageReceived = context =>
            {
                Debug.WriteLine("====== OnMesasge Received Was Called ======");
                return Task.CompletedTask;
            }
        };
    }); 
  1. In Startup.cs, add app.UseAuthentication(); to the Configure method before the call to app.UseAuthorization();.
  2. Put a break point on the return Task.CompletedTask; line in OnMessageReceived.
  3. Run the Application.
  4. Note that the service output is shown without hitting the break point.
  5. Refresh the page.
  6. Note that the break point is hit (because after the first call was done, the rest of the lambda was resumed and finished, adding the event handler.)
  7. Remove the async keyword from line with AddJwtBearer.
  8. Remove the line with await Task.Delay(500); in it.
  9. Run the Application.
  10. Note that the break point is hit before the service output is shown, because the lambda was all executed synchronously.

Basically, when the await is hit, the call continues and the rest of the setup in the lambda is ignored (for the first call).

Expected:

  • I would expect that AddJwtBearer would be called at startup (instead of during the first call)
  • Or, if it needs to be during the first call, it would not allow the call to continue till it completes, (it needs to await the lambda).

Further technical details

  • ASP.NET Core version: 3.1
  • Include the output of `dotnet --info
.NET Core SDK (reflecting any global.json):
 Version:   3.1.101
 Commit:    b377529961

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.18362
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\3.1.101\

Host (useful for support):
  Version: 3.1.1
  Commit:  a1388f194c

.NET Core SDKs installed:
  2.1.505 [C:\Program Files\dotnet\sdk]
  2.1.509 [C:\Program Files\dotnet\sdk]
  2.1.602 [C:\Program Files\dotnet\sdk]
  2.1.604 [C:\Program Files\dotnet\sdk]
  2.1.801 [C:\Program Files\dotnet\sdk]
  2.2.202 [C:\Program Files\dotnet\sdk]
  2.2.204 [C:\Program Files\dotnet\sdk]
  2.2.401 [C:\Program Files\dotnet\sdk]
  3.0.100-preview8-013656 [C:\Program Files\dotnet\sdk]
  3.0.100-preview9-014004 [C:\Program Files\dotnet\sdk]
  3.1.101 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.0.0-preview8.19405.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.0.0-preview8-28405-07 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.0.0-preview8-28405-07 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  • The IDE (VS / VS Code/ VS4Mac) you’re running on, and it’s version Visual Studio 2019 (16.4.5)

Issue Analytics

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

github_iconTop GitHub Comments

1reaction
Tratchercommented, Apr 14, 2020

One way to deal with this could be dealt with by updating the documentation to be clear that the lambda can’t work with async . So people don’t fall in the trap of thinking they can use async there.

The contract is Action<JwtBearerOptions>, Action is never async, an async contract would be Func<JwtBearerOptions, Task>. That’s a C# language level pattern, a doc comment on a specific API does not seem like the appropriate place to explain that.

@stephentoub any thoughts on making a runtime analyzer for “Don’t use async/await with Action lambdas.”? Is this a common mistake you’ve seen?

As kind of related question, why does the configuration for AddJwtBearer happen during the first call? (Instead of startup.)

That has to do with the IOptions infrastructure and auth handler lifetimes. The JwtBearer auth handler is created per request when you try to perform authentication and IOptions resolves its options the first time it’s used.

0reactions
weichchcommented, Apr 16, 2020

@Tratcher

Action is never async, an async contract would be Func<JwtBearerOptions, Task>. That’s a C# language level pattern

Though this is true, async void is also a C# language level pattern which in this case caused the confusion. I liked that if there is an analyzer that can help people identify potential problems with async void actions.

Read more comments on GitHub >

github_iconTop Results From Across the Web

JwtBearerEvents.OnMessageReceived not Called for First ...
UPDATE: The lambda is an Action method. It does not return anything. So trying to do asynchrony in it is not possible without...
Read more >
Troubleshoot invocation issues in Lambda
Invocation errors can be caused by issues with request parameters, event structure, function settings, user permissions, resource permissions, or limits. If you ...
Read more >
ASP.NET Core 3.1 - JWT Authentication Tutorial with ...
Below are instructions on how to use Postman to authenticate a user to get a JWT token from the api, and then make...
Read more >
Secure ASP.NET Core API with JWT Authentication
Token Authentication in WebAPI is pretty Smart & Simple! In this In-Depth Guide, let's learn How to Secure ASP.NET Core API with JWT ......
Read more >
Asynchronous invocation - AWS Lambda
When you invoke a Lambda function asynchronously, Lambda places the request in a queue and returns a success response without additional information.
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