diff --git a/src/Microsoft.AspNet.Server.WebListener/AuthenticationHandler.cs b/src/Microsoft.AspNet.Server.WebListener/AuthenticationHandler.cs
new file mode 100644
index 0000000000..a6a8afe19f
--- /dev/null
+++ b/src/Microsoft.AspNet.Server.WebListener/AuthenticationHandler.cs
@@ -0,0 +1,148 @@
+// Copyright (c) Microsoft Open Technologies, Inc.
+// All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
+// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
+// NON-INFRINGEMENT.
+// See the Apache 2 License for the specific language governing
+// permissions and limitations under the License.
+
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// -----------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Microsoft.AspNet.HttpFeature.Security;
+using Microsoft.Net.Server;
+
+namespace Microsoft.AspNet.Server.WebListener
+{
+ internal class AuthenticationHandler : IAuthenticationHandler
+ {
+ private RequestContext _requestContext;
+ private AuthenticationTypes _authTypes;
+ private AuthenticationTypes _customChallenges;
+
+ internal AuthenticationHandler(RequestContext requestContext)
+ {
+ _requestContext = requestContext;
+ _authTypes = requestContext.AuthenticationChallenges;
+ _customChallenges = AuthenticationTypes.None;
+ }
+
+ public void Authenticate(IAuthenticateContext context)
+ {
+ var user = _requestContext.User;
+ var identity = user == null ? null : (ClaimsIdentity)user.Identity;
+
+ foreach (var authType in ListEnabledAuthTypes())
+ {
+ string authString = authType.ToString();
+ if (context.AuthenticationTypes.Contains(authString, StringComparer.Ordinal))
+ {
+ if (identity != null && identity.IsAuthenticated
+ && string.Equals(authString, identity.AuthenticationType, StringComparison.Ordinal))
+ {
+ context.Authenticated((ClaimsIdentity)user.Identity, properties: null, description: GetDescription(user.Identity.AuthenticationType));
+ }
+ else
+ {
+ context.NotAuthenticated(authString, properties: null, description: GetDescription(user.Identity.AuthenticationType));
+ }
+ }
+ }
+ }
+
+ public Task AuthenticateAsync(IAuthenticateContext context)
+ {
+ Authenticate(context);
+ return Task.FromResult(0);
+ }
+
+ public void Challenge(IChallengeContext context)
+ {
+ foreach (var authType in ListEnabledAuthTypes())
+ {
+ var authString = authType.ToString();
+ // Not including any auth types means it's a blanket challenge for any auth type.
+ if (context.AuthenticationTypes == null || context.AuthenticationTypes.Count == 0
+ || context.AuthenticationTypes.Contains(authString, StringComparer.Ordinal))
+ {
+ _customChallenges |= authType;
+ context.Accept(authString, GetDescription(authType.ToString()));
+ }
+ }
+ // A challenge was issued, it overrides any pre-set auth types.
+ _requestContext.AuthenticationChallenges = _customChallenges;
+ }
+
+ public void GetDescriptions(IAuthTypeContext context)
+ {
+ // TODO: Caching, this data doesn't change per request.
+ foreach (var authType in ListEnabledAuthTypes())
+ {
+ context.Accept(GetDescription(authType.ToString()));
+ }
+ }
+
+ public void SignIn(ISignInContext context)
+ {
+ // Not supported
+ }
+
+ public void SignOut(ISignOutContext context)
+ {
+ // Not supported
+ }
+
+ private IDictionary GetDescription(string authenticationType)
+ {
+ return new Dictionary()
+ {
+ { "AuthenticationType", authenticationType },
+ { "Caption", "Windows:" + authenticationType },
+ };
+ }
+
+ private IEnumerable ListEnabledAuthTypes()
+ {
+ // Order by strength.
+ if ((_authTypes & AuthenticationTypes.Kerberos) == AuthenticationTypes.Kerberos)
+ {
+ yield return AuthenticationTypes.Kerberos;
+ }
+ if ((_authTypes & AuthenticationTypes.Negotiate) == AuthenticationTypes.Negotiate)
+ {
+ yield return AuthenticationTypes.Negotiate;
+ }
+ if ((_authTypes & AuthenticationTypes.NTLM) == AuthenticationTypes.NTLM)
+ {
+ yield return AuthenticationTypes.NTLM;
+ }
+ /*if ((_authTypes & AuthenticationTypes.Digest) == AuthenticationTypes.Digest)
+ {
+ // TODO:
+ throw new NotImplementedException("Digest challenge generation has not been implemented.");
+ yield return AuthenticationTypes.Digest;
+ }*/
+ if ((_authTypes & AuthenticationTypes.Basic) == AuthenticationTypes.Basic)
+ {
+ yield return AuthenticationTypes.Basic;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Server.WebListener/FeatureContext.cs b/src/Microsoft.AspNet.Server.WebListener/FeatureContext.cs
index f1876bcf89..f028582f03 100644
--- a/src/Microsoft.AspNet.Server.WebListener/FeatureContext.cs
+++ b/src/Microsoft.AspNet.Server.WebListener/FeatureContext.cs
@@ -70,6 +70,7 @@ namespace Microsoft.AspNet.Server.WebListener
{
_requestContext = requestContext;
_features = new FeatureCollection();
+ _authHandler = new AuthenticationHandler(requestContext);
PopulateFeatures();
}
@@ -109,14 +110,11 @@ namespace Microsoft.AspNet.Server.WebListener
_features.Add(typeof(IHttpWebSocketFeature), this);
}
- // TODO:
- // _environment.CallCancelled = _cts.Token;
- // _environment.User = _request.User;
- // Channel binding
-
+ // TODO:
/*
- // Server
+ Server
_environment.Listener = _server;
+ Channel binding
_environment.ConnectionId = _request.ConnectionId;
*/
}
diff --git a/src/Microsoft.AspNet.Server.WebListener/Microsoft.AspNet.Server.WebListener.kproj b/src/Microsoft.AspNet.Server.WebListener/Microsoft.AspNet.Server.WebListener.kproj
index 1b81c12fac..e4001830ea 100644
--- a/src/Microsoft.AspNet.Server.WebListener/Microsoft.AspNet.Server.WebListener.kproj
+++ b/src/Microsoft.AspNet.Server.WebListener/Microsoft.AspNet.Server.WebListener.kproj
@@ -21,6 +21,7 @@
+
diff --git a/src/Microsoft.Net.Server/AuthenticationManager.cs b/src/Microsoft.Net.Server/AuthenticationManager.cs
index 64ee0b4d7d..e7b1ab6e71 100644
--- a/src/Microsoft.Net.Server/AuthenticationManager.cs
+++ b/src/Microsoft.Net.Server/AuthenticationManager.cs
@@ -65,6 +65,10 @@ namespace Microsoft.Net.Server
}
set
{
+ if (_authTypes == AuthenticationTypes.None)
+ {
+ throw new ArgumentException("value", "'None' is not a valid authentication type. Use 'AllowAnonymous' instead.");
+ }
_authTypes = value;
SetServerSecurity();
}
@@ -106,22 +110,25 @@ namespace Microsoft.Net.Server
}
}
- // TODO: If we're not going to support Digest then this whole list can be pre-computed and cached.
- // consider even pre-serialzing and caching the bytes for the !AllowAnonymous scenario.
- internal IList GenerateChallenges()
+ internal static IList GenerateChallenges(AuthenticationTypes authTypes)
{
IList challenges = new List();
+ if (authTypes == AuthenticationTypes.None)
+ {
+ return challenges;
+ }
+
// Order by strength.
- if ((_authTypes & AuthenticationTypes.Kerberos) == AuthenticationTypes.Kerberos)
+ if ((authTypes & AuthenticationTypes.Kerberos) == AuthenticationTypes.Kerberos)
{
challenges.Add("Kerberos");
}
- if ((_authTypes & AuthenticationTypes.Negotiate) == AuthenticationTypes.Negotiate)
+ if ((authTypes & AuthenticationTypes.Negotiate) == AuthenticationTypes.Negotiate)
{
challenges.Add("Negotiate");
}
- if ((_authTypes & AuthenticationTypes.Ntlm) == AuthenticationTypes.Ntlm)
+ if ((authTypes & AuthenticationTypes.NTLM) == AuthenticationTypes.NTLM)
{
challenges.Add("NTLM");
}
@@ -131,7 +138,7 @@ namespace Microsoft.Net.Server
throw new NotImplementedException("Digest challenge generation has not been implemented.");
// challenges.Add("Digest");
}*/
- if ((_authTypes & AuthenticationTypes.Basic) == AuthenticationTypes.Basic)
+ if ((authTypes & AuthenticationTypes.Basic) == AuthenticationTypes.Basic)
{
// TODO: Realm
challenges.Add("Basic");
@@ -139,9 +146,9 @@ namespace Microsoft.Net.Server
return challenges;
}
- internal void SetAuthenticationChallenge(Response response)
+ internal void SetAuthenticationChallenge(RequestContext context)
{
- IList challenges = GenerateChallenges();
+ IList challenges = GenerateChallenges(context.AuthenticationChallenges);
if (challenges.Count > 0)
{
@@ -149,7 +156,7 @@ namespace Microsoft.Net.Server
// Append to the existing header, if any. Some clients (IE, Chrome) require each challenges to be sent on their own line/header.
string[] oldValues;
string[] newValues;
- if (response.Headers.TryGetValue(HttpKnownHeaderNames.WWWAuthenticate, out oldValues))
+ if (context.Response.Headers.TryGetValue(HttpKnownHeaderNames.WWWAuthenticate, out oldValues))
{
newValues = new string[oldValues.Length + challenges.Count];
Array.Copy(oldValues, newValues, oldValues.Length);
@@ -160,7 +167,7 @@ namespace Microsoft.Net.Server
newValues = new string[challenges.Count];
challenges.CopyTo(newValues, 0);
}
- response.Headers[HttpKnownHeaderNames.WWWAuthenticate] = newValues;
+ context.Response.Headers[HttpKnownHeaderNames.WWWAuthenticate] = newValues;
}
}
@@ -184,10 +191,30 @@ namespace Microsoft.Net.Server
&& requestInfo->pInfo->AuthStatus == UnsafeNclNativeMethods.HttpApi.HTTP_AUTH_STATUS.HttpAuthStatusSuccess)
{
#if NET45
- return new WindowsPrincipal(new WindowsIdentity(requestInfo->pInfo->AccessToken));
+ return new WindowsPrincipal(new WindowsIdentity(requestInfo->pInfo->AccessToken,
+ GetAuthTypeFromRequest(requestInfo->pInfo->AuthType).ToString()));
#endif
}
- return new ClaimsPrincipal(new ClaimsIdentity(string.Empty)); // Anonymous / !IsAuthenticated
+ return new ClaimsPrincipal(new ClaimsIdentity()); // Anonymous / !IsAuthenticated
+ }
+
+ private static AuthenticationTypes GetAuthTypeFromRequest(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_AUTH_TYPE input)
+ {
+ switch (input)
+ {
+ case UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeBasic:
+ return AuthenticationTypes.Basic;
+ // case UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeDigest:
+ // return AuthenticationTypes.Digest;
+ case UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeNTLM:
+ return AuthenticationTypes.NTLM;
+ case UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeNegotiate:
+ return AuthenticationTypes.Negotiate;
+ case UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeKerberos:
+ return AuthenticationTypes.Kerberos;
+ default:
+ throw new NotImplementedException(input.ToString());
+ }
}
}
}
diff --git a/src/Microsoft.Net.Server/AuthenticationTypes.cs b/src/Microsoft.Net.Server/AuthenticationTypes.cs
index e524d6b69c..9906ba2053 100644
--- a/src/Microsoft.Net.Server/AuthenticationTypes.cs
+++ b/src/Microsoft.Net.Server/AuthenticationTypes.cs
@@ -22,10 +22,10 @@ namespace Microsoft.Net.Server
[Flags]
public enum AuthenticationTypes
{
- // None = 0x0, // None is invalid, use AllowAnonymous (which must have a non-zero value).
+ None = 0x0,
Basic = 0x1,
// Digest = 0x2, // TODO: Verify this is no longer supported by Http.Sys
- Ntlm = 0x4,
+ NTLM = 0x4,
Negotiate = 0x8,
Kerberos = 0x10,
AllowAnonymous = 0x1000
diff --git a/src/Microsoft.Net.Server/RequestProcessing/RequestContext.cs b/src/Microsoft.Net.Server/RequestProcessing/RequestContext.cs
index e0cc636bd1..3864652cef 100644
--- a/src/Microsoft.Net.Server/RequestProcessing/RequestContext.cs
+++ b/src/Microsoft.Net.Server/RequestProcessing/RequestContext.cs
@@ -45,14 +45,15 @@ namespace Microsoft.Net.Server
private CancellationTokenSource _requestAbortSource;
private CancellationToken? _disconnectToken;
- internal RequestContext(WebListener httpListener, NativeRequestContext memoryBlob)
+ internal RequestContext(WebListener server, NativeRequestContext memoryBlob)
{
// TODO: Verbose log
- _server = httpListener;
+ _server = server;
_memoryBlob = memoryBlob;
_request = new Request(this, _memoryBlob);
_response = new Response(this);
_request.ReleasePins();
+ AuthenticationChallenges = server.AuthenticationManager.AuthenticationTypes & ~AuthenticationTypes.AllowAnonymous;
}
public Request Request
@@ -129,6 +130,12 @@ namespace Microsoft.Net.Server
}
}
+ ///
+ /// The authentication challengest that will be added to the response if the status code is 401.
+ /// This must be a subset of the AuthenticationTypes enabled on the server.
+ ///
+ public AuthenticationTypes AuthenticationChallenges { get; set; }
+
public bool IsUpgradableRequest
{
get { return _request.IsUpgradable; }
diff --git a/src/Microsoft.Net.Server/RequestProcessing/Response.cs b/src/Microsoft.Net.Server/RequestProcessing/Response.cs
index 0bca230c20..d803bb4a23 100644
--- a/src/Microsoft.Net.Server/RequestProcessing/Response.cs
+++ b/src/Microsoft.Net.Server/RequestProcessing/Response.cs
@@ -471,7 +471,7 @@ namespace Microsoft.Net.Server
// 401
if (StatusCode == (ushort)HttpStatusCode.Unauthorized)
{
- RequestContext.Server.AuthenticationManager.SetAuthenticationChallenge(this);
+ RequestContext.Server.AuthenticationManager.SetAuthenticationChallenge(RequestContext);
}
UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE;
diff --git a/src/Microsoft.Net.Server/WebListener.cs b/src/Microsoft.Net.Server/WebListener.cs
index a66dd103f8..66a38efdfc 100644
--- a/src/Microsoft.Net.Server/WebListener.cs
+++ b/src/Microsoft.Net.Server/WebListener.cs
@@ -661,7 +661,8 @@ namespace Microsoft.Net.Server
var requestV2 = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)requestMemory.RequestBlob;
if (!AuthenticationManager.AllowAnonymous && !AuthenticationManager.CheckAuthenticated(requestV2->pRequestInfo))
{
- SendError(requestMemory.RequestBlob->RequestId, HttpStatusCode.Unauthorized, AuthenticationManager.GenerateChallenges());
+ SendError(requestMemory.RequestBlob->RequestId, HttpStatusCode.Unauthorized,
+ AuthenticationManager.GenerateChallenges(AuthenticationManager.AuthenticationTypes));
return false;
}
return true;
diff --git a/test/Microsoft.AspNet.Server.WebListener.FunctionalTests/AuthenticationTests.cs b/test/Microsoft.AspNet.Server.WebListener.FunctionalTests/AuthenticationTests.cs
index eae1a1e89e..db7417482d 100644
--- a/test/Microsoft.AspNet.Server.WebListener.FunctionalTests/AuthenticationTests.cs
+++ b/test/Microsoft.AspNet.Server.WebListener.FunctionalTests/AuthenticationTests.cs
@@ -16,6 +16,7 @@
// permissions and limitations under the License.
using System;
+using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
@@ -31,12 +32,13 @@ namespace Microsoft.AspNet.Server.WebListener
private const string Address = "http://localhost:8080/";
[Theory]
+ [InlineData(AuthenticationTypes.AllowAnonymous)]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
- [InlineData(AuthenticationTypes.Ntlm)]
+ [InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)]
[InlineData(AuthenticationTypes.Basic)]
- [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
+ [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_AllowAnonymous_NoChallenge(AuthenticationTypes authType)
{
using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
@@ -56,7 +58,7 @@ namespace Microsoft.AspNet.Server.WebListener
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
- [InlineData(AuthenticationTypes.Ntlm)]
+ [InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
[InlineData(AuthenticationTypes.Basic)]
public async Task AuthType_RequireAuth_ChallengesAdded(AuthenticationTypes authType)
@@ -75,7 +77,7 @@ namespace Microsoft.AspNet.Server.WebListener
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
- [InlineData(AuthenticationTypes.Ntlm)]
+ [InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
[InlineData(AuthenticationTypes.Basic)]
public async Task AuthType_AllowAnonymousButSpecify401_ChallengesAdded(AuthenticationTypes authType)
@@ -101,7 +103,7 @@ namespace Microsoft.AspNet.Server.WebListener
using (Utilities.CreateAuthServer(
AuthenticationTypes.Kerberos
| AuthenticationTypes.Negotiate
- | AuthenticationTypes.Ntlm
+ | AuthenticationTypes.NTLM
/* | AuthenticationTypes.Digest TODO: Not implemented */
| AuthenticationTypes.Basic
| AuthenticationTypes.AllowAnonymous,
@@ -123,10 +125,10 @@ namespace Microsoft.AspNet.Server.WebListener
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
- [InlineData(AuthenticationTypes.Ntlm)]
+ [InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
// [InlineData(AuthenticationTypes.Basic)] // Doesn't work with default creds
- [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /* AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
+ [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /* AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_AllowAnonymousButSpecify401_Success(AuthenticationTypes authType)
{
int requestId = 0;
@@ -159,10 +161,10 @@ namespace Microsoft.AspNet.Server.WebListener
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
- [InlineData(AuthenticationTypes.Ntlm)]
+ [InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
// [InlineData(AuthenticationTypes.Basic)] // Doesn't work with default creds
- [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /* AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
+ [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /* AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_RequireAuth_Success(AuthenticationTypes authType)
{
using (Utilities.CreateAuthServer(authType, env =>
@@ -178,6 +180,216 @@ namespace Microsoft.AspNet.Server.WebListener
}
}
+ [Theory]
+ [InlineData(AuthenticationTypes.AllowAnonymous)]
+ [InlineData(AuthenticationTypes.Kerberos)]
+ [InlineData(AuthenticationTypes.Negotiate)]
+ [InlineData(AuthenticationTypes.NTLM)]
+ // [InlineData(AuthenticationTypes.Digest)]
+ [InlineData(AuthenticationTypes.Basic)]
+ // [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
+ public async Task AuthTypes_GetSingleDescriptions(AuthenticationTypes authType)
+ {
+ using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
+ {
+ var context = new DefaultHttpContext((IFeatureCollection)env);
+ var resultList = context.GetAuthenticationTypes();
+ if (authType == AuthenticationTypes.AllowAnonymous)
+ {
+ Assert.Equal(0, resultList.Count());
+ }
+ else
+ {
+ Assert.Equal(1, resultList.Count());
+ var result = resultList.First();
+ Assert.Equal(authType.ToString(), result.AuthenticationType);
+ Assert.Equal("Windows:" + authType.ToString(), result.Caption);
+ }
+
+ return Task.FromResult(0);
+ }))
+ {
+ var response = await SendRequestAsync(Address);
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(0, response.Headers.WwwAuthenticate.Count);
+ }
+ }
+
+ [Fact]
+ public async Task AuthTypes_GetMultipleDescriptions()
+ {
+ AuthenticationTypes authType =
+ AuthenticationTypes.Kerberos
+ | AuthenticationTypes.Negotiate
+ | AuthenticationTypes.NTLM
+ | /*AuthenticationTypes.Digest
+ |*/ AuthenticationTypes.Basic;
+ using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
+ {
+ var context = new DefaultHttpContext((IFeatureCollection)env);
+ var resultList = context.GetAuthenticationTypes();
+ Assert.Equal(4, resultList.Count());
+ return Task.FromResult(0);
+ }))
+ {
+ var response = await SendRequestAsync(Address);
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(0, response.Headers.WwwAuthenticate.Count);
+ }
+ }
+
+ [Theory]
+ [InlineData(AuthenticationTypes.Kerberos)]
+ [InlineData(AuthenticationTypes.Negotiate)]
+ [InlineData(AuthenticationTypes.NTLM)]
+ // [InlineData(AuthenticationTypes.Digest)]
+ [InlineData(AuthenticationTypes.Basic)]
+ [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
+ public async Task AuthTypes_AuthenticateWithNoUser_NoResults(AuthenticationTypes authType)
+ {
+ var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
+ {
+ var context = new DefaultHttpContext((IFeatureCollection)env);
+ Assert.NotNull(context.User);
+ Assert.False(context.User.Identity.IsAuthenticated);
+ var authResults = context.Authenticate(authTypeList);
+ Assert.False(authResults.Any());
+ return Task.FromResult(0);
+ }))
+ {
+ var response = await SendRequestAsync(Address);
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(0, response.Headers.WwwAuthenticate.Count);
+ }
+ }
+
+ [Theory]
+ [InlineData(AuthenticationTypes.Kerberos)]
+ [InlineData(AuthenticationTypes.Negotiate)]
+ [InlineData(AuthenticationTypes.NTLM)]
+ // [InlineData(AuthenticationTypes.Digest)]
+ // [InlineData(AuthenticationTypes.Basic)] // Doesn't work with default creds
+ [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
+ public async Task AuthTypes_AuthenticateWithUser_OneResult(AuthenticationTypes authType)
+ {
+ var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ using (Utilities.CreateAuthServer(authType, env =>
+ {
+ var context = new DefaultHttpContext((IFeatureCollection)env);
+ Assert.NotNull(context.User);
+ Assert.True(context.User.Identity.IsAuthenticated);
+ var authResults = context.Authenticate(authTypeList);
+ Assert.Equal(1, authResults.Count());
+ return Task.FromResult(0);
+ }))
+ {
+ var response = await SendRequestAsync(Address, useDefaultCredentials: true);
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
+ }
+
+ [Theory]
+ [InlineData(AuthenticationTypes.Kerberos)]
+ [InlineData(AuthenticationTypes.Negotiate)]
+ [InlineData(AuthenticationTypes.NTLM)]
+ // [InlineData(AuthenticationTypes.Digest)]
+ [InlineData(AuthenticationTypes.Basic)]
+ [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
+ public async Task AuthTypes_ChallengeWithoutAuthTypes_AllChallengesSent(AuthenticationTypes authType)
+ {
+ var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
+ {
+ var context = new DefaultHttpContext((IFeatureCollection)env);
+ Assert.NotNull(context.User);
+ Assert.False(context.User.Identity.IsAuthenticated);
+ context.Response.Challenge();
+ return Task.FromResult(0);
+ }))
+ {
+ var response = await SendRequestAsync(Address);
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ Assert.Equal(authTypeList.Count(), response.Headers.WwwAuthenticate.Count);
+ }
+ }
+
+ [Theory]
+ [InlineData(AuthenticationTypes.Kerberos)]
+ [InlineData(AuthenticationTypes.Negotiate)]
+ [InlineData(AuthenticationTypes.NTLM)]
+ // [InlineData(AuthenticationTypes.Digest)]
+ [InlineData(AuthenticationTypes.Basic)]
+ [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
+ public async Task AuthTypes_ChallengeWithAllAuthTypes_AllChallengesSent(AuthenticationTypes authType)
+ {
+ var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
+ {
+ var context = new DefaultHttpContext((IFeatureCollection)env);
+ Assert.NotNull(context.User);
+ Assert.False(context.User.Identity.IsAuthenticated);
+ context.Response.Challenge(authTypeList);
+ return Task.FromResult(0);
+ }))
+ {
+ var response = await SendRequestAsync(Address);
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ Assert.Equal(authTypeList.Count(), response.Headers.WwwAuthenticate.Count);
+ }
+ }
+
+ [Theory]
+ [InlineData(AuthenticationTypes.Kerberos)]
+ [InlineData(AuthenticationTypes.Negotiate)]
+ [InlineData(AuthenticationTypes.NTLM)]
+ // [InlineData(AuthenticationTypes.Digest)]
+ [InlineData(AuthenticationTypes.Basic)]
+ public async Task AuthTypes_ChallengeOneAuthType_OneChallengeSent(AuthenticationTypes authType)
+ {
+ var authTypes = AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic;
+ using (Utilities.CreateAuthServer(authTypes | AuthenticationTypes.AllowAnonymous, env =>
+ {
+ var context = new DefaultHttpContext((IFeatureCollection)env);
+ Assert.NotNull(context.User);
+ Assert.False(context.User.Identity.IsAuthenticated);
+ context.Response.Challenge(authType.ToString());
+ return Task.FromResult(0);
+ }))
+ {
+ var response = await SendRequestAsync(Address);
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ Assert.Equal(1, response.Headers.WwwAuthenticate.Count);
+ Assert.Equal(authType.ToString(), response.Headers.WwwAuthenticate.First().Scheme);
+ }
+ }
+
+ [Theory]
+ [InlineData(AuthenticationTypes.Kerberos)]
+ [InlineData(AuthenticationTypes.Negotiate)]
+ [InlineData(AuthenticationTypes.NTLM)]
+ // [InlineData(AuthenticationTypes.Digest)]
+ [InlineData(AuthenticationTypes.Basic)]
+ public async Task AuthTypes_ChallengeDisabledAuthType_Error(AuthenticationTypes authType)
+ {
+ var authTypes = AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic;
+ authTypes = authTypes & ~authType;
+ var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ using (Utilities.CreateAuthServer(authTypes | AuthenticationTypes.AllowAnonymous, env =>
+ {
+ var context = new DefaultHttpContext((IFeatureCollection)env);
+ Assert.NotNull(context.User);
+ Assert.False(context.User.Identity.IsAuthenticated);
+ Assert.Throws(() => context.Response.Challenge(authType.ToString()));
+ return Task.FromResult(0);
+ }))
+ {
+ var response = await SendRequestAsync(Address);
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ Assert.Equal(0, response.Headers.WwwAuthenticate.Count);
+ }
+ }
+
private async Task SendRequestAsync(string uri, bool useDefaultCredentials = false)
{
HttpClientHandler handler = new HttpClientHandler();
diff --git a/test/Microsoft.Net.Server.FunctionalTests/AuthenticationTests.cs b/test/Microsoft.Net.Server.FunctionalTests/AuthenticationTests.cs
index ce57a86d35..503b515b7e 100644
--- a/test/Microsoft.Net.Server.FunctionalTests/AuthenticationTests.cs
+++ b/test/Microsoft.Net.Server.FunctionalTests/AuthenticationTests.cs
@@ -16,10 +16,10 @@ namespace Microsoft.Net.Server
[InlineData(AuthenticationTypes.AllowAnonymous)]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
- [InlineData(AuthenticationTypes.Ntlm)]
+ [InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)]
[InlineData(AuthenticationTypes.Basic)]
- [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
+ [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_AllowAnonymous_NoChallenge(AuthenticationTypes authType)
{
using (var server = Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous))
@@ -29,6 +29,14 @@ namespace Microsoft.Net.Server
var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
+ if (authType == AuthenticationTypes.AllowAnonymous)
+ {
+ Assert.Equal(AuthenticationTypes.None, context.AuthenticationChallenges);
+ }
+ else
+ {
+ Assert.Equal(authType, context.AuthenticationChallenges);
+ }
context.Dispose();
var response = await responseTask;
@@ -40,7 +48,7 @@ namespace Microsoft.Net.Server
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
- [InlineData(AuthenticationTypes.Ntlm)]
+ [InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented
[InlineData(AuthenticationTypes.Basic)]
public async Task AuthType_RequireAuth_ChallengesAdded(AuthenticationTypes authType)
@@ -60,7 +68,7 @@ namespace Microsoft.Net.Server
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
- [InlineData(AuthenticationTypes.Ntlm)]
+ [InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
[InlineData(AuthenticationTypes.Basic)]
public async Task AuthType_AllowAnonymousButSpecify401_ChallengesAdded(AuthenticationTypes authType)
@@ -72,6 +80,7 @@ namespace Microsoft.Net.Server
var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
+ Assert.Equal(authType, context.AuthenticationChallenges);
context.Response.StatusCode = 401;
context.Dispose();
@@ -84,19 +93,20 @@ namespace Microsoft.Net.Server
[Fact]
public async Task MultipleAuthTypes_AllowAnonymousButSpecify401_ChallengesAdded()
{
- using (var server = Utilities.CreateAuthServer(
+ AuthenticationTypes authType =
AuthenticationTypes.Kerberos
| AuthenticationTypes.Negotiate
- | AuthenticationTypes.Ntlm
+ | AuthenticationTypes.NTLM
/* | AuthenticationTypes.Digest TODO: Not implemented */
- | AuthenticationTypes.Basic
- | AuthenticationTypes.AllowAnonymous))
+ | AuthenticationTypes.Basic;
+ using (var server = Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous))
{
Task responseTask = SendRequestAsync(Address);
var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
+ Assert.Equal(authType, context.AuthenticationChallenges);
context.Response.StatusCode = 401;
context.Dispose();
@@ -109,10 +119,10 @@ namespace Microsoft.Net.Server
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
- [InlineData(AuthenticationTypes.Ntlm)]
+ [InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
// [InlineData(AuthenticationTypes.Basic)] // Doesn't work with default creds
- [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /*AuthenticationType.Digest |*/ AuthenticationTypes.Basic)]
+ [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationType.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_AllowAnonymousButSpecify401_Success(AuthenticationTypes authType)
{
using (var server = Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous))
@@ -122,12 +132,14 @@ namespace Microsoft.Net.Server
var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
+ Assert.Equal(authType, context.AuthenticationChallenges);
context.Response.StatusCode = 401;
context.Dispose();
context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.True(context.User.Identity.IsAuthenticated);
+ Assert.Equal(authType, context.AuthenticationChallenges);
context.Dispose();
var response = await responseTask;
@@ -138,10 +150,10 @@ namespace Microsoft.Net.Server
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
- [InlineData(AuthenticationTypes.Ntlm)]
+ [InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
// [InlineData(AuthenticationTypes.Basic)] // Doesn't work with default creds
- [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /*AuthenticationType.Digest |*/ AuthenticationTypes.Basic)]
+ [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationType.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_RequireAuth_Success(AuthenticationTypes authType)
{
using (var server = Utilities.CreateAuthServer(authType))
@@ -151,6 +163,7 @@ namespace Microsoft.Net.Server
var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.True(context.User.Identity.IsAuthenticated);
+ Assert.Equal(authType, context.AuthenticationChallenges);
context.Dispose();
var response = await responseTask;