dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.43k stars 10.01k forks source link

Write custom authentication failure information to the response body #44100

Open AlexandreBossard opened 2 years ago

AlexandreBossard commented 2 years ago

Is there an existing issue for this?

Is your feature request related to a problem? Please describe the problem.

I have written my Authentication Handler (loosely based on JwtBearHandler). Obviously, I have some AuthenticateResult.fail(ex). All that is good. But, I would like to extract info from these errors and translate them to the Response body aside from the expected 401.

Outside the authentication / authentication framework, I've written an exception handler that does exactly that for controllers.

Unfortunately, I've failed to find a way to retrieve the AuthenticateResult produced by the authentication Handler. IAuthorizationMiddlewareResultHandler do expose the AuthenticateResult but not when the authentication fails, only when it succeed, through HttpContext.Features.Get<IAuthenticateResultFeature>().

Describe the solution you'd like

I would like some ways to get the AuthenticationResult and generate a response body from it. I suppose I could do that from the IAuthorizationMiddlewareResultHandler assuming I had access to the result, regardless of the authentication state. I would throw from the resultHandler which would be translated by the exception handler.

Or am I missing something here ? Any Documentation to point me to ?

Additional context

No response

Tratcher commented 2 years ago

Have you looked at this part of the JwtBearer handler? https://github.com/dotnet/aspnetcore/blob/e13e3facfb26643fe580be56f2d3f466cd5ee009/src/Security/Authentication/JwtBearer/src/JwtBearerHandler.cs#L214

When a user isn't authorized then Challenge will get invoked and the controller won't be executed. Challenge is responsible for generating the response. This example sets the status code and headers, but writing to the body is possible. Note we don't always recommend writing to the body from auth handlers, there are cases where that won't work such as if you have multiple auth handlers enabled/challenged.

AlexandreBossard commented 2 years ago

Yes, I'm actually using it, but not to write a response. Let me explain my setup and why I think using the events to write a response is not good enough. We use exceptions to report errors through our service, Those errors are all handled by the exception handler and transformed in a JSON body which can be understand by our client. This work reasonably well and have the advantages to enforce a known and limited set of errors which are transformed in only one place.

We have authentication setup with JWT with multiple tenants. We want to report 2 different types of error that could happened during authentication (token is invalid and token has expired). The IAuthorizationMiddlewareResultHandler does not expose the AuthenticateResult when the authentication fails either through one HttpContext features (however it does when succeed) or the PolicyAuthorizationResult. So the authentication process result is lost to us, the exception(s) raised by the security token validation are not reported. This prevent us to analyze the cause of the authentication failure to report a richer error than a simple 401.

The only workaround I have found is to use JWTBearerEvents.OnChallenged to set a custom feature on the HTTPContext, like the one when authentication succeed, that could check in the middleware Result Handler.

While this works, I think it's rather convoluted, to have to set events and all registered scheme and to insert a custom feature to get access to the authentication exceptions.

But again, I may have missed something, this framework is big.

adityamandaleeka commented 2 years ago

cc: @blowdart

blowdart commented 2 years ago

Feels like a feature request to me, but yea, it's an odd thing, the body doesn't really come into play for pretty much every auth type, it's a status code thing. Even failing with cookie auth ends up with a redirect, not something that renders. Interestingly AAD failures do end up in a failed page, with details, if you're in the msft domain. But then that's at the login failed level, during the HTML flow to trigger the auth in the first place.

We can look for 8, but I don't know how clean it would be, or worse, how it'd be misunderstood and misused.

ghost commented 2 years ago

Thanks for contacting us.

We're moving this issue to the .NET 8 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

brockallen commented 2 years ago

Feels like a feature request to me, but yea, it's an odd thing, the body doesn't really come into play for pretty much every auth type, it's a status code thing. Even failing with cookie auth ends up with a redirect, not something that renders. Interestingly AAD failures do end up in a failed pa

It's common for bearer token 401/403 responses to need to add to the response headers.

https://www.rfc-editor.org/rfc/rfc6750#section-3

IIRC this was difficult from authorization policies. Maybe it's easier now.

Tratcher commented 2 years ago

It's common for bearer token 401/403 responses to need to add to the response headers.

JwtBearer does that now, but it doesn't include any AuthZ details.