7

I'm quite frustrated about the fact that an authentication scheme appears to be mandatory in Asp.Net Core. My objective is to build an API and I don't want to know anything about the client. I've built custom authentication and authorization, which works fine. I'm not using identity or cookies. However, I can't return a 403 Forbid result without a valid authentication scheme, otherwise I get the following exception...

System.InvalidOperationException: No authentication handler is configured to handle the scheme: Automatic

My question is, can I configure MVC to not use an authentication scheme or create an authentication scheme without the reliance on a login path or any path for that matter?

2
  • 1
    Please post some code so we can help you figure it out. Commented Oct 1, 2016 at 13:16
  • 1
    @Brad This is more of an architectural question. There's no specific code that I'm having an issue with. Basically if you use an Authorize attribute or most things dealing with User.Identity, you require an Authentication Scheme of some sort. Commented Oct 1, 2016 at 14:54

1 Answer 1

3

After poring over the Asp.net Core security source code, I've managed to create a custom authentication handler. To do this you need to implement 3 classes.

The first class implements an abstract AuthenticationOptions.

public class AwesomeAuthenticationOptions : AuthenticationOptions {
    public AwesomeAuthenticationOptions() {
        AuthenticationScheme = "AwesomeAuthentication";
        AutomaticAuthenticate = false;
    }
}

The second class implements an abstract AuthenticationHandler.

public class AwesomeAuthentication : AuthenticationHandler<AwesomeAuthenticationOptions>
{
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var prop = new AuthenticationProperties();
        var ticket = new AuthenticationTicket(Context.User, prop, "AwesomeAuthentication");
        //this is where you setup the ClaimsPrincipal
        //if auth fails, return AuthenticateResult.Fail("reason for failure");
        return await Task.Run(() => AuthenticateResult.Success(ticket));
    }
}

The third class implements an abstract AuthenticationMiddleware.

public class AwesomeAuthenticationMiddleware : AuthenticationMiddleware<AwesomeAuthenticationOptions>
{
    public AwesomeAuthenticationMiddleware(RequestDelegate next, 
        IOptions<AwesomeAuthenticationOptions> options,
        ILoggerFactory loggerFactory,
        UrlEncoder urlEncoder) : base(next, options, loggerFactory, urlEncoder) {

    }

    protected override AuthenticationHandler<AwesomeAuthenticationOptions> CreateHandler()
    {
        return new AwesomeAuthentication();
    }
}

Finally, you use the middleware component in the Startup.cs Configure method.

app.UseMiddleware<AwesomeAuthenticationMiddleware>();

Now you can build your own Authentication Schemes.

Sign up to request clarification or add additional context in comments.

9 Comments

You don't need, and shouldn't do return Task.Run(). Do return AuthenticateResult.Success(ticket); instead.
Also generally if you have no other authentication middleware Context.User is going to be the anonymous user. The whole point of an Authentication handler is to set everything up to populate Context.User. Using it inside HandleAuthenticateAsync() is prone to failure.
@blowdart I've corrected the returning of Task.Run(). I understand the point of this is to setup the principal, but I added this code as an outline over here.
Also when I use the attribute [Authorize("PolicyName")] and the use isn't authenticated, I receive a 403. At this point I'm expecting a 401. Any idea why?
Thanks for the feedback. It is tricky, but I think I'm getting the hang of it. Once I implemented the AuthenticationHandler correctly, everything started working as expected. I didn't have to override the handlers for unauthorized and forbidden as they did what I wanted. Returning AuthenticateResult.Fail("reason"); is what sets the IsAuthenticated on the principal to false, which then returns a 401 on a challenge.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.