Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core - getting a message back from AuthenticationHandler

I have implemented a subclass of AuthenticationHandler. It returns AuthenticationResult.Fail("This is why you can't log in");

I would have expected this message to end up in the body, or at least in the HTTP status text, but instead I get a blank 401 response.

Is there any way to provide additional information for failed authentication attempts in ASP.NET core?

like image 400
Kir Avatar asked Apr 24 '19 16:04

Kir


2 Answers

Override HandleChallengeAsync:

In the example below the failReason is a private field in my implementation of AuthenticationHandler. I don't know if this is the best way to pass the reason for failure. But the AuthenticationProperties on the AuthenticateResult.Fail method did not make it through to HandleChallengeAsync in my test.

public class CustomAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions> where TOptions : AuthenticationSchemeOptions, new()
{
    private string failReason;

    public CustomAuthenticationHandler(IOptionsMonitor<TOptions> options
        , ILoggerFactory logger
        , UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        failReason = "Reason for auth fail";
        return AuthenticateResult.Fail(failReason);
    }

    protected override Task HandleChallengeAsync(AuthenticationProperties properties)
    {
        Response.StatusCode = 401;

        if (failReason != null)
        {
            Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = failReason;
        }

        return Task.CompletedTask;
    }
}

From the docs: https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationhandler-1?view=aspnetcore-2.2

Override this method to deal with 401 challenge concerns, if an authentication scheme in question deals an authentication interaction as part of it's request flow. (like adding a response header, or changing the 401 result to 302 of a login page or external sign-in location.)

Source: https://github.com/aspnet/Security/blob/master/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs#L201

like image 96
user136470 Avatar answered Oct 18 '22 05:10

user136470


I used this code in my custom Middleware to return problemDetails response.

public async Task Invoke(HttpContext httpContext)
{
    await this.Next(httpContext);

    if (httpContext.Response.StatusCode == StatusCodes.Status401Unauthorized)
    {
        var authenticateResult = await httpContext.AuthenticateAsync();

        if (authenticateResult.Failure != null)
        {
            var routeData = httpContext.GetRouteData() ?? new RouteData();
            var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());

            var problemDetails = this.ProblemDetailsFactory.CreateProblemDetails(httpContext,
                statusCode: httpContext.Response.StatusCode,
                detail: authenticateResult.Failure.Message);

            var result = new ObjectResult(problemDetails)
            {
                ContentTypes = new MediaTypeCollection(),
                StatusCode = problemDetails.Status,
                DeclaredType = problemDetails.GetType()
            };

            await this.Executor.ExecuteAsync(actionContext, result);
        }
    }
}
like image 24
Tomáš Holan Avatar answered Oct 18 '22 05:10

Tomáš Holan