Skip to content
This repository was archived by the owner on Nov 1, 2018. It is now read-only.

Auth 2.0 + IISListener #338

Closed
wants to merge 14 commits into from
Closed

Auth 2.0 + IISListener #338

wants to merge 14 commits into from

Conversation

HaoK
Copy link
Member

@HaoK HaoK commented Mar 20, 2017

@Tratcher

Given that ForwardWindows is on by default, not sure making auth optional is going to fly. All of the tests blew up with Auth missing until I added them all...

@dnfclas
Copy link

dnfclas commented Mar 20, 2017

@HaoK,
Thanks for having already signed the Contribution License Agreement. Your agreement was validated by .NET Foundation. We will now review your pull request.
Thanks,
.NET Foundation Pull Request Bot

@Tratcher
Copy link
Member

Tratcher commented Mar 20, 2017

I really wish we had a way of detecting at startup if/which WindowsAuth schemes were enabled in IIS. @shirhatti, @pan-wang? It's hard to implement a correct IIS auth handler without knowing, and we don't want people to have to configure it in two places.

{
context.NotAuthenticated();
}
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(user, Scheme.Name)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note we must not call Merge on this principal, we need to keep the original for IsInRole to work. Same for WebListener.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yuck, ok so that basically means you can't really combine the windows auth with the other auth types or the merging will break things

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but those scenarios don't usually overlap. E.g. you don't do NTLM + Cookies at the same time. So long as we can make Merge smart enough not to merge when there's really just WindowsPrincipal and Anonymous it should be fine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You made sure this identity never gets merged right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No we'd have to add something to the security helper (or the authorize filter) to special case this somehow.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay so by default we don't have to do anything, merge is only called if the effective policy ends up with multiple schemes requested, which requires a "config error" to have been introduced

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main scenario = DefaultAuthenticate/ChallengeScheme = "Windows" correct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're registering both Negotiate and NTLM separately right now. The experience would be better if you combine them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then you wouldn't have to specify a default

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we calling this Windows too like HttpSys?

return PriorHandler.SignInAsync(context);
case ChallengeBehavior.Automatic:
// If there is a principal already, invoke the forbidden code path
if (GetUser(context.HttpContext) == null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetUser needs to cache if this is going to work, it disposes the handle so it can't run more than once.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated with GetUser caching

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It only caches on success though, not sure if we expect this to be called repeatedly on failures or if we care...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fine, the failure scenarios are primarily for a missing header which will be fast.

@HaoK
Copy link
Member Author

HaoK commented Apr 24, 2017

Updated with latests auth changes and packages, tests are all passing now

var auth = authentication.FirstOrDefault();
if (auth == null)
{
throw new InvalidOperationException("IServiceCollection.AddAuthentication() is required to use Authentication.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this exception for now, ForwardWindowsAuthentication is enabled by default.

throw new InvalidOperationException("IServiceCollection.AddAuthentication() is required to use Authentication.");
}

auth.AddScheme(new AuthenticationScheme("NTLM", /*displayName*/ null,typeof(AuthenticationHandler)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

named params instead of inline comments?

<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.HttpOverrides" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(AspNetCoreVersion)" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert duplicate

@@ -191,7 +201,7 @@ public void PathBaseHiddenFromServer()
{
app.Run(context =>
{
var auth = context.Features.Get<IHttpAuthenticationFeature>();
var auth = context.RequestServices.GetService<IAuthenticationSchemeProvider>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not equivalent. You should still add the auth service in this case, it's testing the ForwardWindowsAuthentication flag. You may clone this test to make sure everything also works without the auth service.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated test to verify handler only added when forward = true, also added a variant that ensures things don't blow up without auth services.

@@ -11,6 +11,8 @@

<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IISIntegration\Microsoft.AspNetCore.Server.IISIntegration.csproj" />
<ProjectReference Include="..\..\..\Security\src\Microsoft.AspNetCore.Authentication\Microsoft.AspNetCore.Authentication.csproj" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert :-)

using Microsoft.Extensions.Logging;

namespace TestSites
{
public class StartupHttpsHelloWorld
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthenticationCore();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auth shouldn't be needed in this scenario

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is because of the functional tests calling Challenge

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes only needed in the Ntlm one.

using Microsoft.Extensions.Logging;

namespace TestSites
{
public class StartupHelloWorld
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthenticationCore();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auth shouldn't be needed in this scenario

var negotiate = await auth.GetSchemeAsync("Negotiate");
Assert.NotNull(negotiate);
Assert.Equal("Microsoft.AspNetCore.Server.IISIntegration.AuthenticationHandler", negotiate.HandlerType.FullName);
var windows = await auth.GetSchemeAsync("Windows");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File a followup issue to figure out how we can optionally handle granular schemes. This will be more relevant for HttpSys, but the solution will likely apply to both.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -24,6 +25,7 @@ public class IISMiddlewareTests
.UseSetting("PORT", "12345")
.UseSetting("APPL_PATH", "/")
.UseIISIntegration()
.ConfigureServices(services => services.AddAuthentication())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only add this to tests that need it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, done

Assert.NotNull(auth);
Assert.Equal("Microsoft.AspNetCore.Server.IISIntegration.AuthenticationHandler", auth.Handler.GetType().FullName);
var windowsAuth = await auth.GetSchemeAsync("Windows");
if (forward)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

else assert null? or would GetSchemeAsync throw?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup I forgot to add the else check null, thanks :)

@@ -150,15 +156,62 @@ public void PathBaseHiddenFromServer()
.UseSetting("PORT", "12345")
.UseSetting("APPL_PATH", "/")
.UseIISIntegration()
.ConfigureServices(services => services.AddAuthentication())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make a copy of this test without AddAuthentication and test auth.GetSchemeAsync("Windows"); returns null.

Copy link
Member Author

@HaoK HaoK Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well we can't even call GetScheme without AddAuthentication as the SchemeProvider will be null, we already have tests that verify that the server can start without AddAuthentication.

}

if (context.Request.Path.Equals("/AutoForbid"))
{
return context.Authentication.ChallengeAsync();
return context.ChallengeAsync();
}

if (context.Request.Path.Equals("/RestrictedNegotiate"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove RestrictedNegotiate, it's not used in the tests.

response = await httpClient.GetAsync("/RestrictedNTLM");
responseText = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
Assert.Contains("NTLM", response.Headers.WwwAuthenticate.ToString());
// Note we can't restrict a challenge to a specific auth type, the native auth modules always add themselves.
Assert.Contains("Negotiate", response.Headers.WwwAuthenticate.ToString());

@@ -12,6 +13,7 @@ public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication();
Copy link
Member

@Tratcher Tratcher May 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also need to add the auth middleware if you want HttpContext.User to get set without calling Authenticate, right? E.g. this sample doesn't work right now does it? Oh, the auth middelware is in the security repo, you can't reference it here can you?

HttpSysServer doesn't have this problem because it sets User before the app is invoked. What if the IISMiddleware always set the User? That code in GetUser should be run on every request regardless to clean up handles. The IISMiddleware could resolve the auth service, resolve the handler, and invoke it directly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't try running the samples, perhaps mistakenly I assumed functional tests were a superset, urgh yeah forgot we can't depend on security in this repo :/

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windows auth doesn't really need the AuthN middleware, it could do it itself / resolve and invoke the service directly.

@HaoK
Copy link
Member Author

HaoK commented May 11, 2017

Updated as we discussed, if ForwardWindowsAuth, add Windows scheme, and always set it to httpContext.User if found.

await _next(httpContext);
}
finally
var result = await httpContext.AuthenticateAsync("Windows");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constant for Windows. Public static readonly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IISMiddleware.AuthenticationScheme

@@ -76,6 +76,7 @@ public Task NtlmAuthentication(RuntimeArchitecture architecture, ApplicationType
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("Anonymous?True", responseText);

/* Disabled for now
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the skip url here

@@ -12,6 +13,7 @@ public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthenticationCore();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be removed from samples and tests now that it's added by the main component.

@@ -12,6 +12,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.AspNetCoreModule" Version="$(AspNetCoreModuleVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Core" Version="$(AspNetCoreVersion)" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra?

@@ -11,6 +11,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Core" Version="$(AspNetCoreVersion)" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra?

@HaoK
Copy link
Member Author

HaoK commented May 12, 2017

Updated

@@ -18,6 +18,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
{
public class IISMiddleware
{
public static readonly string AuthenticationScheme = "Windows";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to follow the pattern from Security? E.g. IISDefaults.AuthenticationScheme?
https://github.com/aspnet/Security/blob/dev/src/Microsoft.AspNetCore.Authentication.Google/GoogleDefaults.cs#L11
Note those are public const string so they can be used in attributes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure IISDefaults.AuthenticationScheme, and HttpSysDefaults.AuthenticationScheme for the other repo?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

{
Assert.NotNull(windowsAuth);
Assert.Null(windowsAuth.DisplayName);
Assert.Equal("AuthenticationHandler", windowsAuth.HandlerType.Name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vague?

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task DoesNotBlowUpWithoutAuth(bool forward)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not relevant anymore? Auth is always added.

@Tratcher
Copy link
Member

You'll need to update ServerTests too.

@HaoK
Copy link
Member Author

HaoK commented May 17, 2017

8ed21d5

@HaoK HaoK closed this May 17, 2017
@natemcmaster natemcmaster deleted the haok/auth branch November 1, 2018 06:04
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants