An Async `AddJwtBearer` Lambda does not Complete for First Call
See original GitHub issueDescribe 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
- Create a new .Net Core Web Application
- Select that you are making an API from the wizard
- Add the
Microsoft.AspNetCore.Authentication.JwtBearer
NuGet - In
Startup.cs
, addusing Microsoft.AspNetCore.Authentication.JwtBearer;
to the top of the file. - In
Startup.cs
, add the following code to theConfigureServices
method before the call toServices.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;
}
};
});
- In
Startup.cs
, addapp.UseAuthentication();
to theConfigure
method before the call toapp.UseAuthorization();
. - Put a break point on the
return Task.CompletedTask;
line inOnMessageReceived
. - Run the Application.
- Note that the service output is shown without hitting the break point.
- Refresh the page.
- 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.)
- Remove the
async
keyword from line withAddJwtBearer
. - Remove the line with
await Task.Delay(500);
in it. - Run the Application.
- 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:
- Created 3 years ago
- Comments:6 (5 by maintainers)
Top 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 >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
The contract is
Action<JwtBearerOptions>
, Action is never async, an async contract would beFunc<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?
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.
@Tratcher
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 withasync void
actions.