Skip to content
This repository was archived by the owner on Mar 19, 2019. It is now read-only.

Prototype Auth 2.0 changes #325

Closed
wants to merge 10 commits into from
Closed

Prototype Auth 2.0 changes #325

wants to merge 10 commits into from

Conversation

HaoK
Copy link
Member

@HaoK HaoK commented Mar 13, 2017

@Tratcher thoughts? Also we need to update/replace the auth tests since MessagePump is being new'ed up and we need RequestServices for auth to work...

foreach (var scheme in ListEnabledAuthSchemes())
{
var authScheme = scheme.ToString();
// Not including any auth types means it's a blanket challenge for any auth type.
if (automaticChallenge || string.Equals(context.AuthenticationScheme, authScheme, StringComparison.Ordinal))
if (string.Equals(context.AuthenticationScheme, authScheme, StringComparison.Ordinal))
Copy link
Member Author

Choose a reason for hiding this comment

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

I actually don't think we need this check at all anymore, since we are registering a handler per scheme

Copy link
Member Author

Choose a reason for hiding this comment

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

We'd just need to check if the scheme is enabled to determine if we return anything or not

@HaoK
Copy link
Member Author

HaoK commented Mar 13, 2017

Tests are broken due to RequestServices being null so this is just an early look at how things stand.

options.AddScheme("Negotiate", builder => builder.HandlerType = typeof(AuthenticationHandler));
options.AddScheme("NTLM", builder => builder.HandlerType = typeof(AuthenticationHandler));
options.AddScheme("Basic", builder => builder.HandlerType = typeof(AuthenticationHandler));
options.AddScheme("None", builder => builder.HandlerType = 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.

Remove None

Copy link
Member

Choose a reason for hiding this comment

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

Lets move this to server Start. See MessagePump

@@ -91,13 +81,23 @@ public Task ChallengeAsync(ChallengeContext context)
return TaskCache.CompletedTask;
}

public void GetDescriptions(DescribeSchemesContext context)
public Task<bool> HandleRequestAsync()
Copy link
Member

Choose a reason for hiding this comment

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

Didn't you move this to a separate interface?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep this PR was created against Iteration 2 (prior to named changes and the split of HandleRequest)

I'll update this PR now against latest

@@ -112,14 +112,14 @@ public Task SignOutAsync(SignOutContext context)
return TaskCache.CompletedTask;
Copy link
Member

Choose a reason for hiding this comment

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

These comments about !Accepted aren't true anymore. SignIn should throw NotSupportedException. SignOut is something we're discussing elsewhere.

@@ -16,14 +17,7 @@ internal class AuthenticationHandler : IAuthenticationHandler
private AuthenticationSchemes _authSchemes;
private AuthenticationSchemes _customChallenges;

internal AuthenticationHandler(RequestContext requestContext)
{
_requestContext = requestContext;
Copy link
Member

Choose a reason for hiding this comment

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

So how do we get access to this now?

@HaoK
Copy link
Member Author

HaoK commented Mar 16, 2017

Updated with fixes, register the handlers in MessagePump Start instead which is much cleaner, luckily it doesn't take any options... there's no story yet for how to configure handler options via dynamic AddScheme....

@@ -32,7 +33,7 @@ internal class MessagePump : IServer

private readonly ServerAddressesFeature _serverAddresses;

public MessagePump(IOptions<HttpSysOptions> options, ILoggerFactory loggerFactory)
Copy link
Member

Choose a reason for hiding this comment

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

For this to work UseHttpSysServer has to register the 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.

Well maybe AddAuthentication needs to be added as part of hosting similar to options, its kinda required...

Copy link
Member

Choose a reason for hiding this comment

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

It's required for HttpSysServer, but only if auth is enabled. Same for IIS Integration. There are plenty of apps that don't need it though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Probably need to factor the new auth stuff into the 'core stuff' (and the security base class stuff). Hosting should add the core stuff so everyone can add schemes...

Copy link
Member

Choose a reason for hiding this comment

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

Resolve it as optional?

Copy link
Member Author

Choose a reason for hiding this comment

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

this mimics how anyone could hook their own IAuthenticationHandler on httpContext today, its always there today

Copy link
Member Author

Choose a reason for hiding this comment

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

Well do we really want context.AuthenticateAsync to throw due to no service found if AddAuthentication isn't called?

Copy link
Member

Choose a reason for hiding this comment

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

Sounds fine. Why Authenticate without Authentication?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok so we are leaving it as an exercise for the user to ensure something is calling AddAuthentication, MVC/identity will do this, as well as all of our security projects

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 to resolve IAuthenticationSchemeProvider as enumerable and only throw if AuthentionSchemes for the server options is not none.

@@ -9,7 +9,9 @@
<PackageTags>aspnetcore;weblistener;httpsys</PackageTags>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="1.2.0-*" />
<ProjectReference Include="..\..\..\Hosting\src\Microsoft.AspNetCore.Hosting\Microsoft.AspNetCore.Hosting.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.

Authentication or Authentication.Abstractions? These are getting move to HttpAbstractions right? Otherwise this is going to cause a layering issue.

Copy link
Member Author

Choose a reason for hiding this comment

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

Should be abstractions yes

@Tratcher
Copy link
Member

Looks pretty good. Still need help writing the tests?

@HaoK
Copy link
Member Author

HaoK commented Mar 20, 2017

Yeah tests don't work at all here, (request services are null). I'm working on the PR for IISIntegration now.

Copy link
Member

@Tratcher Tratcher left a comment

Choose a reason for hiding this comment

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

Try adding this to Utilities:



        internal static IWebHost CreateDynamicHost(string basePath, AuthenticationSchemes authType, bool allowAnonymous, out string root, out string baseAddress, RequestDelegate app)
        {
            lock (PortLock)
            {
                while (NextPort < MaxPort)
                {
                    var port = NextPort++;
                    var prefix = UrlPrefix.Create("http", "localhost", port, basePath);
                    root = prefix.Scheme + "://" + prefix.Host + ":" + prefix.Port;
                    baseAddress = prefix.ToString();

                    var builder = new WebHostBuilder()
                        .UseHttpSys(options =>
                        {
                            options.UrlPrefixes.Add(prefix);
                            options.Authentication.Schemes = authType;
                            options.Authentication.AllowAnonymous = allowAnonymous;
                        })
                        .Configure(appBuilder => appBuilder.Run(app));

                    var host = builder.Build();


                    try
                    {
                        host.Start();
                        return host;
                    }
                    catch (HttpSysException)
                    {
                    }

                }
                NextPort = BasePort;
            }
            throw new Exception("Failed to locate a free port.");
        }

(Untested, but at least a starting point for you)

Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

private static TestServer CreateServer(Action<HttpSysOptions> configureHttpSys = null, Func<HttpContext, Task> testpath = null)
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't belong in the FunctionalTest project. Move to unit tests.

@HaoK
Copy link
Member Author

HaoK commented Mar 27, 2017

Updated after switching to your new dynamic server and things are almost working. Challenge is the last test that is broken, I'll comment on the test to explain the issue

throw new NotSupportedException(context.Behavior.ToString());
}
}
}
// A challenge was issued, it overrides any pre-set auth types.
_requestContext.Response.AuthenticationChallenges = _customChallenges;
Copy link
Member Author

Choose a reason for hiding this comment

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

This set for the _customChallenges no longers works because we have 3 different handlers all trying to challenge at the same time, before they were all the same handler instance, so _customChallenges would have all 3. While now they are different handlers, so we end up only setting the last auth scheme.

{
Assert.NotNull(httpContext.User);
Assert.NotNull(httpContext.User.Identity);
Assert.False(httpContext.User.Identity.IsAuthenticated);
return httpContext.Authentication.ChallengeAsync();
return httpContext.ChallengeAsync(authType.ToString());
Copy link
Member Author

Choose a reason for hiding this comment

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

This test ends up failing since it expects to see all 3 challenges but we only send 1. Not sure how best to fix this behavior.

Copy link
Member

Choose a reason for hiding this comment

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

HTTP auth is one of the few places it makes sense for there to be multiple automatic challenge types. Bearer, Basic, NTLM, Negotiate, Kerberos, and Digest can all be challenged on the same response. That implies DefaultChallengeScheme and DefaultAuthenticationScheme would need to be a list in this scenario.

Copy link
Member Author

@HaoK HaoK Mar 28, 2017

Choose a reason for hiding this comment

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

Meh one of our main goals was to make it impossible to say challenge Bearer + Cookies via DefaultChallengeScheme

Copy link
Member Author

Choose a reason for hiding this comment

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

Since HTTP auth is special, can't it do the multiple challenges itself? Why does it need the ability to be baked in.

Copy link
Member

Choose a reason for hiding this comment

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

How can it do multiple itself? Make one handler for 'Windows' and have it issue challenges for all enabled schemes?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah what's the usage scenario, would that work? Can it be considered a "Host" scheme, and translate that into the AuthenticationSchemes inside of its options? Or do you expect ppl to actually put Authorize(NTLM/Basic/Negotitate)?

Copy link
Member

Choose a reason for hiding this comment

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

Most people would do all or none, but it's an awkward limitation not to be able to invoke them individually. What if you had two sets of handlers? One each for the individuals, and one that combines all of them?

Copy link
Member

Choose a reason for hiding this comment

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

Note IIS would just have one, it can't be more specific

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah that's not too bad, so we can have something like "Windows" which would challenge everything that was enabled on the host, and leave the individual ones as is?

@HaoK
Copy link
Member Author

HaoK commented Apr 26, 2017

Replaced by #354

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.

2 participants