Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.Net core 2: Default Authentication Scheme ignored

I'm trying to build a custom AuthenticationHandler in ASP.Net Core 2. Following up topic like ASP.NET Core 2.0 authentication middleware and Why is Asp.Net Core Authentication Scheme mandatory, I've created the specific classes. The registering happens like this:

services.AddAuthentication(
    options =>
    {
        options.DefaultScheme = Constants.NoOpSchema;
        options.DefaultAuthenticateScheme = Constants.NoOpSchema;
        options.DefaultChallengeScheme = Constants.NoOpSchema;
        options.DefaultSignInScheme = Constants.NoOpSchema;
        options.DefaultSignOutScheme = Constants.NoOpSchema; 
        options.DefaultForbidScheme = Constants.NoOpSchema;
    }
).AddScheme<CustomAuthOptions, CustomAuthHandler>(Constants.NoOpSchema, "Custom Auth", o => { });

Everything works, if the specific controllers set the Scheme explicitely:

[Authorize(AuthenticationSchemes= Constants.NoOpSchema)]
[Route("api/[controller]")]
public class IndividualsController : Controller

But I would like to not have to set the Schema, since it should get added dynamically. As soon as I remove the Scheme Property, like this:

[Authorize]
[Route("api/[controller]")]
public class IndividualsController : Controller

It doesn't work anymore.

I would have hoped, that setting the DefaultScheme Properties does this job. Interesting enough, I didn't find any specific discussion about this topic. Am I doing something wrong here or is my expected outcome wrong?

Edit: Thanks for the questions, it helped me a lot. It seems like mapping the DefaultScheme is using by the Authentication Middleware, which I only used, when the CustomAuthHandler was not in place. Therefore I had to add the AuthenticationMiddleware always.

Edit2: Unfortunately, it still doesn't work. To enhance a bit my question: I'm adding the middleware as usual:

app.UseAuthentication();
app.UseMvc();

Now I get into my Handler, which is looking like this:

public class NoOpAuthHandler : AuthenticationHandler<NoOpAuthOptions>
{
    public const string NoOpSchema = "NoOp";

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

    protected override Task<AuthenticateResult> HandleAuthenticateAsync() => Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(Context.User, NoOpSchema)));
}

But even if I always return success, I get an 401. I think I have to dig deeper and kindahow set some Claims, but unfortunately, the Handlers from Microsoft are quite hard to analyze, since they contain a lot of Code.

like image 882
Matthias Müller Avatar asked Apr 19 '18 08:04

Matthias Müller


2 Answers

ASP.NET Core 2 Answer

You have to set a default authorization policy tied to your authentication scheme:

services.AddAuthorization(options => {
  options.DefaultPolicy = new AuthorizationPolicyBuilder()
    .AddAuthenticationSchemes(Constants.NoOpSchema)
    .RequireAuthenticatedUser()
    .Build();
});

ASP.NET Core 3 Answer

In ASP.NET Core 3 things apparently changed a bit, so you would want to create an extension method to add your authentication handler:

public static class NoOpAuthExtensions
{
    public static AuthenticationBuilder AddNoOpAuth(this AuthenticationBuilder builder)
        => builder.AddNoOpAuth(NoOpAuthHandler.NoOpSchema, _ => { });
    public static AuthenticationBuilder AddNoOpAuth(this AuthenticationBuilder builder, Action<NoOpAuthOptions> configureOptions)
        => builder.AddNoOpAuth(NoOpAuthHandler.NoOpSchema, configureOptions);
    public static AuthenticationBuilder AddNoOpAuth(this AuthenticationBuilder builder, string authenticationScheme, Action<NoOpAuthOptions> configureOptions)
        => builder.AddNoOpAuth(authenticationScheme, null, configureOptions);
    public static AuthenticationBuilder AddNoOpAuth(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<NoOpAuthOptions> configureOptions)
    {
        return builder.AddScheme<NoOpAuthOptions, NoOpAuthHandler>(authenticationScheme, displayName, configureOptions);
    }
}

And use it in your ConfigureServices method like this:

services
  .AddAuthentication(NoOpAuthHandler.NoOpSchema)
  .AddNoOpAuth();
like image 125
RaphaelH Avatar answered Nov 03 '22 16:11

RaphaelH


Make sure that you have Authentication middleware in your pipeline and place it before MVC.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    ///

    app.UseAuthentication();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller}/{action=Index}/{id?}");
    });
    ///
}

UPDATE

try using this code in HandleAuthenticateAsync method.

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
    List<Claim> claims = new List<Claim>();
    ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, Scheme.Name);
    ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
    AuthenticationTicket authenticationTicket = new AuthenticationTicket(claimsPrincipal, Scheme.Name);
    return AuthenticateResult.Success(authenticationTicket);
}
like image 11
Kahbazi Avatar answered Nov 03 '22 17:11

Kahbazi