AuthenticationHandler<TOptions>.ChallengeAsync
isn't called
#2005
Replies: 5 comments 7 replies
-
Hi @horato. I think that ChallengeAsync is typically used when interactive authentication needs to happen for the user. This is not a typical scenario for API's. Perhaps you could explain a little bit more about what you're trying to do so we can help you out? |
Beta Was this translation helpful? Give feedback.
-
Hi, From what I read online, the ChallengeAsync method is to be used to alter HTTP response and to generate WWW-Authenticate header. |
Beta Was this translation helpful? Give feedback.
-
Could you share your Program.cs, Startup.cs and configuration? Maybe I can figure out what's going wrong. |
Beta Was this translation helpful? Give feedback.
-
Here is a quick and dirty sample that seems to ilustrate my issue. If you call GET on http://localhost:10000/gateway/test you will end up in NtlmAuthHandler.HandleAuthenticateAsync, which fails auth. Code in HandleChallengeAsync doesn't get called and the response is plain 401 without any additional data. protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
HandleChallengeAsync(null);
return Task.FromResult(AuthenticateResult.Fail("failed"));
} Then it works as expected: One request is sent without auth, server responds with 401 and WWW-Authenticate NTLM and then second request is sent which contains the auth. Nugets: using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Reflection;
using System.Security.Authentication;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using AuthenticationSchemes = Microsoft.AspNetCore.Server.HttpSys.AuthenticationSchemes;
namespace ConsoleApp2
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, configurationBuilder) => configurationBuilder.AddConfiguration(new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddEnvironmentVariables().Build()))
.ConfigureLogging(x => x.AddConsole())
.UseUrls("http://localhost:10000")
.UseStartup<Startup>()
.UseHttpSys(x =>
{
x.Authentication.AllowAnonymous = true;
x.Authentication.Schemes = AuthenticationSchemes.None;
})
.Build();
host.StartAsync();
Console.ReadLine();
host.StopAsync().Wait();
}
}
public class Startup
{
private const string OcelotConfigPath = "ConsoleApp2.ocelot.json";
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile(OcelotConfigPath, optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication()
.AddScheme<NtlmAuthOptions, NtlmAuthHandler>("Windows", _ => { });
services.AddCors();
services.AddMvc(x => x.EnableEndpointRouting = false).PartManager.ApplicationParts.Add(new AppPart());
services.AddOcelot(Configuration);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors(x => x.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
app.UseMvc();
app.UseOcelot().Wait();
}
}
public class AppPart : ApplicationPart, IApplicationPartTypeProvider
{
public override string Name => "AppPart";
public IEnumerable<TypeInfo> Types => new[] { typeof(TestController).GetTypeInfo() };
}
[Route("test")]
[ApiController]
public class TestController : Controller
{
[HttpGet]
[ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)]
public IActionResult BasicHealthCheck()
{
return Ok();
}
}
public class NtlmAuthHandler : AuthenticationHandler<NtlmAuthOptions>
{
public NtlmAuthHandler(IOptionsMonitor<NtlmAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
return Task.FromResult(AuthenticateResult.Fail("failed"));
}
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
{
return Task.Run(() =>
{
Response.StatusCode = (int)HttpStatusCode.Unauthorized;
Response.Headers.Add(HeaderNames.WWWAuthenticate, "NTLM");
});
}
}
public class NtlmAuthOptions : AuthenticationSchemeOptions
{
}
} Thank you |
Beta Was this translation helpful? Give feedback.
-
Any luck with getting windows auth working with http.sys? I've been trying but not had much success. Is there somewhere with a quick example on how to get this working? |
Beta Was this translation helpful? Give feedback.
-
I am not entierly sure how to fill the issue template, I am sorry.
Is there any reason why AuthenticationHandler.ChallengeAsync doesn't get called when auth fails? Is this by design or am I doing something wrong? Is this implemented?
Thank you
Beta Was this translation helpful? Give feedback.
All reactions