UPDATE (8/7/2014) - The solution to this problem was that I needed to add a class that derived from "UserNamePasswordValidator" and register it in Web.Config.
I have created a simple test WCF service and test console client application (see below for code). I am using .NET 4.5.1. I have already searched for duplicates on StackOverflow (found similar posts here and here) - however I feel that the referenced posts are potentially outdated, and also feel that my post is more limited in scope.
OK now for the example:
The solution currently uses sessions (in ITestService.cs):
[ServiceContract(SessionMode = SessionMode.Required)]
... and uses wsHttpBinding (see below app.config and web.config).
When I deploy this to a server, I am successfully able to access it via a web browser using HTTPS like this: https://myserver.com/test/testservice.svc
However, when I change the endpoint in the client app.config from:
http://localhost:20616/TestService.svc/TestService.svc
to:
https://myserver.com/test/testservice.svc
and run the console application again, I receive the error: "The provided URI scheme 'https' is invalid; expected 'http'. Parameter name: via"
My question is, what is the minimum changes I need to make for this to work, without changing SessionMode.Required?
Here is the client console application code. Please be sure to change the App.Config value for "mycomputer\Matt" to the correct value for your machine.
Program.cs
using System;
namespace TestClient
{
class Program
{
static void Main(string[] args)
{
Console.Clear();
Console.WriteLine("Attempting to log in...");
try
{
TestServiceReference.TestServiceClient client = new TestServiceReference.TestServiceClient();
bool loginSuccess = client.LogIn("admin", "password");
if (loginSuccess)
{
Console.WriteLine("Successfully logged in.");
string secretMessage = client.GetSecretData();
Console.WriteLine("Retrieved secret message: " + secretMessage);
}
else
{
Console.WriteLine("Log in failed!");
}
}
catch (Exception exc)
{
Console.WriteLine("Exception occurred: " + exc.Message);
}
Console.WriteLine("Press ENTER to quit.");
Console.ReadLine();
}
}
}
App.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
</startup>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ITestService"/>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://myserver.com/test/testservice.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ITestService" contract="TestServiceReference.ITestService" name="WSHttpBinding_ITestService">
<identity>
<userPrincipalName value="mycomputer\Matt"/>
</identity>
</endpoint>
<!--<endpoint address="http://localhost:20616/TestService.svc/TestService.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ITestService" contract="TestServiceReference.ITestService" name="WSHttpBinding_ITestService">
<identity>
<userPrincipalName value="mycomputer\Matt"/>
</identity>
</endpoint>-->
</client>
</system.serviceModel>
</configuration>
WCF Service code.
ITestService.cs:
using System.ServiceModel;
namespace WcfSessionsOverHttpsTest
{
[ServiceContract(SessionMode = SessionMode.Required)]
public interface ITestService
{
[OperationContract(IsInitiating = true)]
bool LogIn(string username, string password);
[OperationContract(IsInitiating = false, IsTerminating = true)]
bool LogOut();
[OperationContract(IsInitiating = false)]
string GetSecretData();
}
}
TestService.svc:
namespace WcfSessionsOverHttpsTest
{
public class TestService : ITestService
{
public bool IsAuthenticated { get; set; }
bool ITestService.LogIn(string username, string password)
{
if (username == "admin" && password == "password")
{
IsAuthenticated = true;
return true;
}
else
{
IsAuthenticated = false;
return false;
}
}
bool ITestService.LogOut()
{
IsAuthenticated = false;
return true;
}
string ITestService.GetSecretData()
{
if (!IsAuthenticated)
{
throw new System.Security.Authentication.AuthenticationException("User has not logged in.");
}
else
{
string secretMessage = "The Red Sox are going to win the World Series in 2016";
return secretMessage;
}
}
}
}
Web.config:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5.1"/>
<httpRuntime targetFramework="4.5"/>
</system.web>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttpEndpointBinding" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="WcfSessionsOverHttpsTest.TestService">
<endpoint address="/TestService.svc" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" contract="WcfSessionsOverHttpsTest.ITestService"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="wsHttpBinding" scheme="http"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
Thanks in advance for any help!
Matt
The solution to this problem was that I needed to add a class that derived from "UserNamePasswordValidator" and register it in Web.Config.
public class CustomUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
return;
}
}
Web.config:
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpsGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyProgram.CustomUserNameValidator,MyProgram" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>