dotnet / dotnet/aspnetcore

[Authorize(AuthenticationSchemes = TheOnlyAuthScheme)] also runs default scheme AuthenticationHandler

Open
#47,105 14 comments 10 reactions 0 assignees View on GitHub
area-auth Docs Pillar: Technical Debt
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Describe the bug

The documentation https://learn.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme?view=aspnetcore-7.0 says:

> The [[Authorize]](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authorization.authorizeattribute) attribute specifies the authentication scheme or schemes to use **regardless of whether a default is configured**.

```
[Authorize(AuthenticationSchemes=JwtBearerDefaults.AuthenticationScheme)]
public class Mixed2Controller : Controller
{
public ContentResult Index() => Content(MyWidgets.GetMyContent());
}
```

> In the preceding code, only the handler with the "Bearer" scheme runs. Any cookie-based identities are ignored.
Note: `JwtBearerDefaults.AuthenticationScheme` is the "Bearer" string constant here.

This is not true for me.
When a default authentication scheme is registered, its authentication handler will be executed upon success of the "only" authentication scheme which is requested by the annotation `[Authorize(AuthenticationSchemes=JwtBearerDefaults.AuthenticationScheme)]` on the REST API controller or REST API controller method.

Repro project:
https://github.com/janseris/ASPNetCoreMultipleAuthentications

How to repro:
1) run project
2) in Swagger UI, click green Authorize button which enables you to enter HTTP Authorization header content to be passed in to the controller
![image](https://user-images.githubusercontent.com/64279914/223900712-6ca0624c-043d-4057-b0d2-7bb38462d2ff.png)

3) In the Session ID form, enter in value `SessionID 123` which passes the validation in the SessionID `AuthenticationHandler` and click green Authorize button to apply
- this will add HTTP Authorization header value `SessionID 123` to all the HTTP requests executed via the Swagger UI
![image](https://user-images.githubusercontent.com/64279914/223900638-18fb48fc-e31f-412b-bb25-51bf11413340.png)

4) execute a controller method via Swagger UI by clicking the method, then Try it Out and then blue Execute button
![image](https://user-images.githubusercontent.com/64279914/223900967-787e43d0-983d-4c90-9380-8ddd7e3cc0d9.png)

5) see debug output in Visual Studio - you will see that the Session ID authentication handler executed and finished and then HTTP Basic authentication handler executed (**unexpected**) _which you can also observe in the browser because the a prompt for HTTP Basic authentication credentials will show as a result of failed HTTP Basic authentication (because the authentication/authorization header did not contain a valid HTTP Basic authentication value because it was `SessionID 123`_)
![image](https://user-images.githubusercontent.com/64279914/223901101-725b937d-dab3-4e9a-a0f1-0efedb7a4db6.png)

### Expected Behavior

When `[Authorize(AuthenticationSchemes=JwtBearerDefaults.AuthenticationScheme)]` annotation is used, only that single AuthenticationHandler (registered under the authentication scheme name `JwtBearerDefaults.AuthenticationScheme` is executed).

That in my case would be the session ID authentication handler specified on the controller method:
```

public class ItemsController : ControllerBase
{
//only "SessionID" authentication schema handler should be executed.
[Authorize(AuthenticationSchemes = SessionIDAuthenticationHandler.AuthenticationSchemeName)]
[HttpGet("", Name = "GetAllItems")]
[ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)]
public Task> GetAll()
{
var items = new List
{
new Item
{
Name = "item1"
},
new Item
{
Name = "item2"
}
};
return Task.FromResult(items.AsEnumerable());
}
}
```

### Steps To Reproduce

Repro project:
https://github.com/janseris/ASPNetCoreMultipleAuthentications

Repro steps:
1) run project
2) in Swagger UI, click green Authorize button which enables you to enter HTTP Authorization header content to be passed in to the controller
![image](https://user-images.githubusercontent.com/64279914/223900712-6ca0624c-043d-4057-b0d2-7bb38462d2ff.png)

3) In the Session ID form, enter in value `SessionID 123` which passes the validation in the SessionID `AuthenticationHandler` and click green Authorize button to apply
- this will add HTTP Authorization header value `SessionID 123` to all the HTTP requests executed via the Swagger UI
![image](https://user-images.githubusercontent.com/64279914/223900638-18fb48fc-e31f-412b-bb25-51bf11413340.png)

4) execute a controller method via Swagger UI by clicking the method, then Try it Out and then blue Execute button
![image](https://user-images.githubusercontent.com/64279914/223900967-787e43d0-983d-4c90-9380-8ddd7e3cc0d9.png)

5) see debug output in Visual Studio - you will see that the Session ID authentication handler executed and finished and the HTTP Basic authentication handler executed (**unexpected**) which you can also observe in the browser because the default browser dialog for HTTP Basic authentication will show
![image](https://user-images.githubusercontent.com/64279914/223901101-725b937d-dab3-4e9a-a0f1-0efedb7a4db6.png)

### Exceptions (if any)

_No response_

### .NET Version

.NET SDK 7.0.200 commit 534117727b

### Anything else?

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.