Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AuthenticationHandler<TOptions>.ChallengeAsync isn't called #1144

Closed
horato opened this issue Feb 23, 2020 · 5 comments
Closed

AuthenticationHandler<TOptions>.ChallengeAsync isn't called #1144

horato opened this issue Feb 23, 2020 · 5 comments
Assignees
Labels
Authentication Ocelot feature: Authentication question Initially seen a question could become a new feature or bug or closed ;) waiting Waiting for answer to question or feedback from issue raiser

Comments

@horato
Copy link

horato commented Feb 23, 2020

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

@jmezach
Copy link
Contributor

jmezach commented Feb 24, 2020

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?

@jmezach jmezach self-assigned this Feb 24, 2020
@jmezach jmezach added question Initially seen a question could become a new feature or bug or closed ;) waiting Waiting for answer to question or feedback from issue raiser labels Feb 24, 2020
@horato
Copy link
Author

horato commented Feb 24, 2020

Hi,
Our ocelot gateway has two endpoints - /domain/* and /client/*. Domain route uses basic auth and client route uses NTLM. We have one AuthHandler for basic auth.
We used HttpSys for NTLM auth, but it seems that it always validates basic auth login:password with local active directory. This results in the account being permanently locked because basic auth password does not match the domain password.
I turned off all of HttpSys' auth schemes and created custom auth handler for NTLM. This also prevents WWW-Authenticate header from being generated by HttpSys and the response is plain 401 Unauthorized.

From what I read online, the ChallengeAsync method is to be used to alter HTTP response and to generate WWW-Authenticate header.

@jmezach
Copy link
Contributor

jmezach commented Feb 24, 2020

Could you share your Program.cs, Startup.cs and configuration? Maybe I can figure out what's going wrong.

@horato
Copy link
Author

horato commented Feb 25, 2020

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.
When you edit the HandleAuthenticateAsync like this:

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.
I am testing this using SaopUI without preemptive authorization.

Nugets:
Microsoft.AspNetCore 2.2.0
Microsoft.AspNetCore.Mvc 2.2.0
Microsoft.AspNetCore.Server.HttpSys 2.2.6
Ocelot 13.8.4 (because .NETCoreApp v3.0, I am sure latest would have the same result)

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

@candreouTrade
Copy link

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?

@ThreeMammals ThreeMammals locked and limited conversation to collaborators Mar 23, 2024
@raman-m raman-m converted this issue into discussion #2005 Mar 23, 2024
@raman-m raman-m added the Authentication Ocelot feature: Authentication label Jun 17, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
Authentication Ocelot feature: Authentication question Initially seen a question could become a new feature or bug or closed ;) waiting Waiting for answer to question or feedback from issue raiser
Projects
None yet
Development

No branches or pull requests

4 participants