-
Notifications
You must be signed in to change notification settings - Fork 39
Prototype Auth 2.0 changes #325
Conversation
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)) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
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)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove None
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolve it as optional?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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" /> |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be abstractions yes
Looks pretty good. Still need help writing the tests? |
Yeah tests don't work at all here, (request services are null). I'm working on the PR for IISIntegration now. |
There was a problem hiding this 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) |
There was a problem hiding this comment.
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.
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; |
There was a problem hiding this comment.
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()); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
Replaced by #354 |
@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...