Skip to content

Commit

Permalink
Add support for AuthenticationSchemes.IntegratedWindowsAuthentication (
Browse files Browse the repository at this point in the history
  • Loading branch information
mconnew authored Sep 10, 2020
1 parent 69f4b26 commit 6a76e08
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 21 deletions.
4 changes: 2 additions & 2 deletions src/System.Private.ServiceModel/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -862,10 +862,10 @@
<value>Cannot resolve the host name of URI "{0}" using DNS.</value>
</data>
<data name="HttpRequiresSingleAuthScheme" xml:space="preserve">
<value>The '{0}' authentication scheme has been specified on the HTTP factory. However, the factory only supports specification of exactly one authentication scheme. Valid authentication schemes are Digest, Negotiate, NTLM, Basic, or Anonymous.</value>
<value>The '{0}' authentication scheme has been specified on the HTTP factory. However, the factory only supports specification of exactly one authentication scheme. Valid authentication schemes are Digest, Negotiate, NTLM, Basic, IntegratedWindowsAuthentication, or Anonymous.</value>
</data>
<data name="HttpAuthSchemeCannotBeNone" xml:space="preserve">
<value>The value specified for the AuthenticationScheme property on the HttpTransportBindingElement ('{0}') is not allowed when building a ChannelFactory. If you used a standard binding, ensure the ClientCredentialType is not set to HttpClientCredentialType.InheritedFromHost, a value which is invalid on a client. If you set the value to '{0}' directly on the HttpTransportBindingElement, please set it to Digest, Negotiate, NTLM, Basic, or Anonymous.</value>
<value>The value specified for the AuthenticationScheme property on the HttpTransportBindingElement ('{0}') is not allowed when building a ChannelFactory. If you used a standard binding, ensure the ClientCredentialType is not set to HttpClientCredentialType.InheritedFromHost, a value which is invalid on a client. If you set the value to '{0}' directly on the HttpTransportBindingElement, please set it to Digest, Negotiate, NTLM, Basic, IntegratedWindowsAuthentication, or Anonymous.</value>
</data>
<data name="HttpAuthorizationFailed" xml:space="preserve">
<value>The HTTP request is unauthorized with client authentication scheme '{0}'. The authentication header received from the server was '{1}'.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ internal HttpChannelFactory(HttpTransportBindingElement bindingElement, BindingC
_httpCookieContainerManager = new HttpCookieContainerManager();
}

if (!bindingElement.AuthenticationScheme.IsSingleton())
if (!bindingElement.AuthenticationScheme.IsSingleton() && bindingElement.AuthenticationScheme != AuthenticationSchemes.IntegratedWindowsAuthentication)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.Format(SR.HttpRequiresSingleAuthScheme,
bindingElement.AuthenticationScheme));
Expand Down Expand Up @@ -328,8 +328,20 @@ internal async Task<HttpClient> GetHttpClientAsync(EndpointAddress to, Uri via,
else
{
CredentialCache credentials = new CredentialCache();
credentials.Add(GetCredentialCacheUriPrefix(via),
AuthenticationSchemesHelper.ToString(AuthenticationScheme), credential);
Uri credentialCacheUriPrefix = GetCredentialCacheUriPrefix(via);
if (AuthenticationScheme == AuthenticationSchemes.IntegratedWindowsAuthentication)
{
credentials.Add(credentialCacheUriPrefix, AuthenticationSchemesHelper.ToString(AuthenticationSchemes.Negotiate),
credential);
credentials.Add(credentialCacheUriPrefix, AuthenticationSchemesHelper.ToString(AuthenticationSchemes.Ntlm),
credential);
}
else
{
credentials.Add(credentialCacheUriPrefix, AuthenticationSchemesHelper.ToString(AuthenticationScheme),
credential);
}

clientHandler.Credentials = credentials;
}
}
Expand Down Expand Up @@ -413,7 +425,7 @@ internal ICredentials GetCredentials()
}
break;
case AuthenticationSchemes.Ntlm:
goto case AuthenticationSchemes.Negotiate;
case AuthenticationSchemes.IntegratedWindowsAuthentication:
case AuthenticationSchemes.Negotiate:
if (credentials.Windows.ClientCredential.UserName != string.Empty)
{
Expand Down Expand Up @@ -451,6 +463,7 @@ private SecurityTokenProviderContainer CreateAndOpenTokenProvider(TimeSpan timeo
break;
case AuthenticationSchemes.Negotiate:
case AuthenticationSchemes.Ntlm:
case AuthenticationSchemes.IntegratedWindowsAuthentication:
tokenProvider = TransportSecurityHelpers.GetSspiTokenProvider(SecurityTokenManager, target, via, Scheme, authenticationScheme, channelParameters);
break;
case AuthenticationSchemes.Digest:
Expand Down Expand Up @@ -617,10 +630,11 @@ protected virtual string OnGetConnectionGroupPrefix(SecurityTokenContainer clien

internal static bool IsWindowsAuth(AuthenticationSchemes authScheme)
{
Contract.Assert(authScheme.IsSingleton(), "authenticationScheme used in an Http(s)ChannelFactory must be a singleton value.");
Fx.Assert(authScheme.IsSingleton() || authScheme == AuthenticationSchemes.IntegratedWindowsAuthentication, "authenticationScheme used in an Http(s)ChannelFactory must be a singleton value.");

return authScheme == AuthenticationSchemes.Negotiate ||
authScheme == AuthenticationSchemes.Ntlm;
authScheme == AuthenticationSchemes.Ntlm ||
authScheme == AuthenticationSchemes.IntegratedWindowsAuthentication;
}

private string GetConnectionGroupName(NetworkCredential credential, AuthenticationLevel authenticationLevel,
Expand Down Expand Up @@ -1336,7 +1350,7 @@ public WebProxyFactory(Uri address, bool bypassOnLocal, AuthenticationSchemes au
_address = address;
_bypassOnLocal = bypassOnLocal;

if (!authenticationScheme.IsSingleton())
if (!authenticationScheme.IsSingleton() && authenticationScheme != AuthenticationSchemes.IntegratedWindowsAuthentication)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(authenticationScheme), SR.Format(SR.HttpRequiresSingleAuthScheme,
authenticationScheme));
Expand Down Expand Up @@ -1376,8 +1390,18 @@ public async Task<IWebProxy> CreateWebProxyAsync(AuthenticationLevel requestAuth
}

CredentialCache credentials = new CredentialCache();
credentials.Add(_address, AuthenticationSchemesHelper.ToString(AuthenticationScheme),
credential);
if (AuthenticationScheme == AuthenticationSchemes.IntegratedWindowsAuthentication)
{
credentials.Add(_address, AuthenticationSchemesHelper.ToString(AuthenticationSchemes.Negotiate),
credential);
credentials.Add(_address, AuthenticationSchemesHelper.ToString(AuthenticationSchemes.Ntlm),
credential);
}
else
{
credentials.Add(_address, AuthenticationSchemesHelper.ToString(AuthenticationScheme),
credential);
}
result.Credentials = credentials;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ private static async Task<NetworkCredential> GetCredentialCoreAsync(Authenticati
break;

case AuthenticationSchemes.Ntlm:
case AuthenticationSchemes.IntegratedWindowsAuthentication: // IWA could use NTLM
result = await TransportSecurityHelpers.GetSspiCredentialAsync(credentialProvider,
impersonationLevelWrapper, authenticationLevelWrapper, timeout);
if (authenticationLevelWrapper.Value == AuthenticationLevel.MutualAuthRequired)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingC
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.Format(SR.HttpAuthSchemeCannotBeNone,
AuthenticationScheme));
}
else if (!AuthenticationScheme.IsSingleton())
else if (!AuthenticationScheme.IsSingleton() && AuthenticationScheme != AuthenticationSchemes.IntegratedWindowsAuthentication)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.Format(SR.HttpRequiresSingleAuthScheme,
AuthenticationScheme));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ internal static HttpClientCredentialType MapToClientCredentialType(Authenticatio
result = HttpClientCredentialType.Ntlm;
break;
case AuthenticationSchemes.Negotiate:
case AuthenticationSchemes.IntegratedWindowsAuthentication:
result = HttpClientCredentialType.Windows;
break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ private bool IsDigestAuthenticationScheme(SecurityTokenRequirement requirement)
{
AuthenticationSchemes authScheme = (AuthenticationSchemes)requirement.Properties[ServiceModelSecurityTokenRequirement.HttpAuthenticationSchemeProperty];

if (!authScheme.IsSingleton())
if (!authScheme.IsSingleton() && authScheme != AuthenticationSchemes.IntegratedWindowsAuthentication)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("authScheme", string.Format(SR.HttpRequiresSingleAuthScheme, authScheme));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

//

using System;
using System.Collections.Generic;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Infrastructure.Common;

using Xunit;

public static class Http_ClientCredentialTypeTests
public class Http_ClientCredentialTypeTests : ConditionalWcfTest
{
[WcfFact]
[OuterLoop]
Expand Down Expand Up @@ -139,4 +136,29 @@ public static void HttpExpect100Continue_DigestAuthentication_True()
ScenarioTestHelpers.CloseCommunicationObjects((ICommunicationObject)serviceProxy, factory);
}
}

[WcfFact]
[Condition(nameof(Windows_Authentication_Available), nameof(Is_Windows))]
[OuterLoop]
public static void WindowsAuthentication_RoundTrips_Echo()
{
BasicHttpBinding basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;

ScenarioTestHelpers.RunBasicEchoTest(basicHttpBinding, Endpoints.Http_WindowsAuth_Address, "BasicHttpBinding with Windows authentication", null);
}

[WcfFact]
[Condition(nameof(Windows_Authentication_Available), nameof(Is_Windows))]
[OuterLoop]
public static void IntegratedWindowsAuthentication_Negotiate_RoundTrips_Echo()
{
BasicHttpBinding basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
var binding = new CustomBinding(basicHttpBinding);
var htbe = binding.Elements.Find<HttpTransportBindingElement>();
htbe.AuthenticationScheme = System.Net.AuthenticationSchemes.IntegratedWindowsAuthentication;

ScenarioTestHelpers.RunBasicEchoTest(binding, Endpoints.Http_WindowsAuth_Address, "BasicHttpBinding with IntegratedWindowsAuthentication authentication and Negotiate endpoint", null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,16 @@ public static void NtlmAuthentication_RoundTrips_Echo()
}

[WcfFact]
[Condition(nameof(Windows_Authentication_Available), nameof(Is_Windows))]
[Condition(nameof(NTLM_Available), nameof(Root_Certificate_Installed))]
[OuterLoop]
public static void WindowsAuthentication_RoundTrips_Echo()
public static void IntegratedWindowsAuthentication_Ntlm_RoundTrips_Echo()
{
BasicHttpBinding basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
BasicHttpBinding basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
var binding = new CustomBinding(basicHttpBinding);
var htbe = binding.Elements.Find<HttpsTransportBindingElement>();
htbe.AuthenticationScheme = System.Net.AuthenticationSchemes.IntegratedWindowsAuthentication;

ScenarioTestHelpers.RunBasicEchoTest(basicHttpBinding, Endpoints.Http_WindowsAuth_Address, "BasicHttpBinding with Windows authentication", null);
ScenarioTestHelpers.RunBasicEchoTest(binding, Endpoints.Https_NtlmAuth_Address, "BasicHttpBinding with IntegratedWindowsAuthentication authentication and Ntlm endpoint", null);
}
}

0 comments on commit 6a76e08

Please sign in to comment.