diff --git a/src/Microsoft.AspNet.Server.WebListener/AuthenticationHandler.cs b/src/Microsoft.AspNet.Server.WebListener/AuthenticationHandler.cs index 225d45a388..aa0e411d72 100644 --- a/src/Microsoft.AspNet.Server.WebListener/AuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Server.WebListener/AuthenticationHandler.cs @@ -45,8 +45,7 @@ namespace Microsoft.AspNet.Server.WebListener public Task AuthenticateAsync(AuthenticateContext context) { - var user = _requestContext.User; - var identity = user == null ? null : (ClaimsIdentity)user.Identity; + var identity = (ClaimsIdentity)_requestContext.User?.Identity; foreach (var authType in ListEnabledAuthSchemes()) { @@ -56,7 +55,7 @@ namespace Microsoft.AspNet.Server.WebListener if (identity != null && identity.IsAuthenticated && string.Equals(authScheme, identity.AuthenticationType, StringComparison.Ordinal)) { - context.Authenticated(new ClaimsPrincipal(user.Identity), properties: null, description: GetDescription(authScheme)); + context.Authenticated(new ClaimsPrincipal(identity), properties: null, description: GetDescription(authScheme)); } else { @@ -73,12 +72,38 @@ namespace Microsoft.AspNet.Server.WebListener { var authScheme = scheme.ToString(); // Not including any auth types means it's a blanket challenge for any auth type. - if (context.AuthenticationScheme == string.Empty || + if (string.IsNullOrEmpty(context.AuthenticationScheme) || string.Equals(context.AuthenticationScheme, authScheme, StringComparison.Ordinal)) { - _requestContext.Response.StatusCode = 401; - _customChallenges |= scheme; - context.Accept(); + switch (context.Behavior) + { + case ChallengeBehavior.Forbidden: + _requestContext.Response.StatusCode = 403; + context.Accept(); + break; + case ChallengeBehavior.Unauthorized: + _requestContext.Response.StatusCode = 401; + _customChallenges |= scheme; + context.Accept(); + break; + case ChallengeBehavior.Automatic: + var identity = (ClaimsIdentity)_requestContext.User?.Identity; + if (identity != null && identity.IsAuthenticated + && string.Equals(identity.AuthenticationType, context.AuthenticationScheme, StringComparison.Ordinal)) + { + _requestContext.Response.StatusCode = 403; + context.Accept(); + } + else + { + _requestContext.Response.StatusCode = 401; + _customChallenges |= scheme; + context.Accept(); + } + break; + default: + throw new NotSupportedException(context.Behavior.ToString()); + } } } // A challenge was issued, it overrides any pre-set auth types. @@ -97,13 +122,13 @@ namespace Microsoft.AspNet.Server.WebListener public Task SignInAsync(SignInContext context) { - // Not supported + // Not supported. AuthenticationManager will throw if !Accepted. return Task.FromResult(0); } public Task SignOutAsync(SignOutContext context) { - // Not supported + // Not supported. AuthenticationManager will throw if !Accepted. return Task.FromResult(0); } diff --git a/test/Microsoft.AspNet.Server.WebListener.FunctionalTests/AuthenticationTests.cs b/test/Microsoft.AspNet.Server.WebListener.FunctionalTests/AuthenticationTests.cs index f802a8d3bb..56478fc87f 100644 --- a/test/Microsoft.AspNet.Server.WebListener.FunctionalTests/AuthenticationTests.cs +++ b/test/Microsoft.AspNet.Server.WebListener.FunctionalTests/AuthenticationTests.cs @@ -21,6 +21,7 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNet.FeatureModel; +using Microsoft.AspNet.Http.Features.Authentication; using Microsoft.AspNet.Http.Internal; using Xunit; using AuthenticationSchemes = Microsoft.Net.Http.Server.AuthenticationSchemes; @@ -411,6 +412,78 @@ namespace Microsoft.AspNet.Server.WebListener } } + [Theory] + [InlineData(AuthenticationSchemes.Kerberos)] + [InlineData(AuthenticationSchemes.Negotiate)] + [InlineData(AuthenticationSchemes.NTLM)] + // [InlineData(AuthenticationSchemes.Digest)] + [InlineData(AuthenticationSchemes.Basic)] + public async Task AuthTypes_Forbid_Forbidden(AuthenticationSchemes authType) + { + string address; + var authTypes = AuthenticationSchemes.AllowAnonymous | AuthenticationSchemes.Kerberos | AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /*AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic; + using (Utilities.CreateHttpAuthServer(authTypes, out address, env => + { + var context = new DefaultHttpContext((IFeatureCollection)env); + Assert.NotNull(context.User); + Assert.False(context.User.Identity.IsAuthenticated); + return context.Authentication.ForbidAsync(authType.ToString()); + })) + { + var response = await SendRequestAsync(address); + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + Assert.Equal(0, response.Headers.WwwAuthenticate.Count); + } + } + + [Theory] + [InlineData(AuthenticationSchemes.Kerberos)] + [InlineData(AuthenticationSchemes.Negotiate)] + [InlineData(AuthenticationSchemes.NTLM)] + // [InlineData(AuthenticationSchemes.Digest)] // Not implemented + // [InlineData(AuthenticationSchemes.Basic)] // Can't log in with UseDefaultCredentials + public async Task AuthTypes_ChallengeAuthenticatedAuthType_Forbidden(AuthenticationSchemes authType) + { + string address; + using (Utilities.CreateHttpAuthServer(authType, out address, env => + { + var context = new DefaultHttpContext((IFeatureCollection)env); + Assert.NotNull(context.User); + Assert.True(context.User.Identity.IsAuthenticated); + return context.Authentication.ChallengeAsync(authType.ToString()); + })) + { + var response = await SendRequestAsync(address, useDefaultCredentials: true); + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + // for some reason Kerberos and Negotiate include a 2nd stage challenge. + // Assert.Equal(0, response.Headers.WwwAuthenticate.Count); + } + } + + [Theory] + [InlineData(AuthenticationSchemes.Kerberos)] + [InlineData(AuthenticationSchemes.Negotiate)] + [InlineData(AuthenticationSchemes.NTLM)] + // [InlineData(AuthenticationSchemes.Digest)] // Not implemented + // [InlineData(AuthenticationSchemes.Basic)] // Can't log in with UseDefaultCredentials + public async Task AuthTypes_UnathorizedAuthenticatedAuthType_Unauthorized(AuthenticationSchemes authType) + { + string address; + using (Utilities.CreateHttpAuthServer(authType, out address, env => + { + var context = new DefaultHttpContext((IFeatureCollection)env); + Assert.NotNull(context.User); + Assert.True(context.User.Identity.IsAuthenticated); + return context.Authentication.ChallengeAsync(authType.ToString(), null, ChallengeBehavior.Unauthorized); + })) + { + var response = await SendRequestAsync(address, useDefaultCredentials: true); + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + Assert.Equal(1, response.Headers.WwwAuthenticate.Count); + Assert.Equal(authType.ToString(), response.Headers.WwwAuthenticate.First().Scheme); + } + } + private async Task SendRequestAsync(string uri, bool useDefaultCredentials = false) { HttpClientHandler handler = new HttpClientHandler();