1

I am trying to extend an existing ASP.NET application with a WCF service. The service must require authentication via the same Membership Provider that the rest of the site uses.

Here's the web.config file:

<configuration>
<system.web>
  <compilation debug="true" targetFramework="4.5.2" />
  <httpRuntime targetFramework="4.5.2" />
  <authentication mode="Forms">
    <forms name=".MyAppAuthentication" timeout="5760" slidingExpiration="true" />
  </authentication>
  <roleManager enabled="true" defaultProvider="AspNetSqlRoleProvider">
    <providers>
      <clear />
      <add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="UsersContext" applicationName="MyApp" />
    </providers>
  </roleManager>
  <membership defaultProvider="AspNetSqlMembershipProvider">
    <providers>
      <clear/>
      <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="UsersContext" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="10" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="MyApp"/>
    </providers>
  </membership>
</system.web>
<system.serviceModel>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  <bindings>
    <wsHttpBinding>
      <binding name="MembershipBinding">
        <security mode="TransportWithMessageCredential">
          <transport clientCredentialType="None" />
          <message clientCredentialType="UserName"/>
        </security>
      </binding>
    </wsHttpBinding>
  </bindings>
  <behaviors>
    <serviceBehaviors>
      <behavior name="MembershipBehaviour">
        <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="false"/>
        <serviceAuthorization roleProviderName="AspNetSqlRoleProvider" principalPermissionMode="UseAspNetRoles" />
        <serviceCredentials>
          <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="AspNetSqlMembershipProvider" />
        </serviceCredentials>
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <services>
    <service name="ServiceTestBackend.Services.TestService" behaviorConfiguration="MembershipBehaviour">
      <endpoint address="" binding="wsHttpBinding" bindingConfiguration="MembershipBinding" contract="ServiceTestBackend.Services.ITestService" />
    </service>
  </services>
</system.serviceModel>
<connectionStrings>
  <add name="UsersContext" connectionString="data source=.;initial catalog=MyApp.Accounts;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>

The code of the service is very simple:

public class TestService : ITestService
{
    public string FindUser()
    {
        var user = Membership.GetUser();
        return user.UserName;
    }
}

And the client is just about as simple

var client = new TestService.TestServiceClient();
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
var acc = client.FindUser();

The server is invoked, but the method fails because Membership.GetUser() returns null.

Am I missing anything in the configuration?

Impworks
  • 2,647
  • 3
  • 25
  • 53
  • You want to get the client identity on the server side, right ? – Emmanuel DURIN Nov 10 '15 at 13:40
  • @EmmanuelDURIN, yes, I expect the code on the server side to be executed with the credentials specified on the client side. – Impworks Nov 10 '15 at 13:43
  • I a just reading your config more carefully, and I think my answer is bad. I just deleted my post. Your code should work... but in the config why do you create a custom binding ? – Emmanuel DURIN Nov 10 '15 at 14:00
  • It was for testing purpose to avoid using SSL. Enabling SSL does not affect the situation anyhow. – Impworks Nov 10 '15 at 14:04
  • Stupid question : does the {username/password} user exists in your db ? If not, the identity cannot be added the current user. – Emmanuel DURIN Nov 10 '15 at 14:43
  • Yes, it exists. When I specify a different password, the breakpoint in the backend method's code is not hit and I get the following error: `At least one security token in the message could not be validated.` – Impworks Nov 10 '15 at 15:12
  • Did you try the post with ServiceSecurityContext.Current ? ( I just undeleted it) – Emmanuel DURIN Nov 11 '15 at 07:49
  • I just tried my post with ServiceSecurityContext, it works - but I get a null from Membership.GetUser(), despite providing MemberShip.ApplicationName - so I ve got good hope it will work for you. – Emmanuel DURIN Nov 11 '15 at 08:23
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/94786/discussion-between-impworks-and-emmanuel-durin). – Impworks Nov 11 '15 at 08:27

2 Answers2

0

I think there is some confusion here. I am not sure what you want to do exactly. You can make this really simple by doing this.

  public class TestService : ITestService
  {
     public string FindUser(string userName, string password)
     {
        var user = Membership.GetUser(username, password);
        return (user!=null?user.userName: string.Empty);
     }
 }

Your client should look like;

 using(var client = new TestService.TestServiceClient())
 {
     var response = client.FindUser(username, password);
     //validate the response
 }
Kosala W
  • 2,133
  • 1
  • 15
  • 20
  • I am trying to configure the service so that it is executed in an authenticated environment. For example, `HttpContext.Current.User` must be set for some parts of the code to work correctly. Passing the credentials explicitly seems like a bad idea because it breaks compatibility and reinvents the wheel. – Impworks Nov 10 '15 at 13:41
  • So the client is not a separate application, but a part of your main application? Which means both client and the server share the same HttpContext? – Kosala W Nov 10 '15 at 13:47
  • No. There are two separate parts: IIS-hosted backend and console frontend. I send a request from the frontend and specify login and password, everything is ok here. But then the backend starts executing code and `HttpContext.Current.User` is empty, as if I did not specify any credentials. This is the undesired behaviour I am trying to fix. – Impworks Nov 10 '15 at 13:52
  • When you say you "send a request from the frontend and specify login and password" and everything is ok, what did you mean exactly? Can you put a breakpoint at the server side authentication method and see if you are receiving the username/password at the server and, the user is getting authenticated properly? As may know already, if the Current.User is null, the user has not been authenticated. – Kosala W Nov 10 '15 at 13:58
  • I do not have an explicitly implemented authentication method. Is it supposed to be performed automatically by the memberhship provider if I specify binding security correctly, or am I wrong? I have updated the config file a little to make it more explciit. – Impworks Nov 10 '15 at 14:33
  • Just a remark : take care of compiling server, and recreate client bindings every time you change the server ! You should have some security bindings that match on the client – Emmanuel DURIN Nov 10 '15 at 15:30
  • @Impworks: If you are thinking of ws-security, this [article](https://msdn.microsoft.com/en-us/library/ms996951.aspx) may help you. But as you can see it needs a bit more work. – Kosala W Nov 10 '15 at 23:17
0
  • Try code like this :
public string FindUser()
{
   if (ServiceSecurityContext.Current != null)
       return ServiceSecurityContext.Current.PrimaryIdentity.Name;
   return null;
}
  • You may as well get user name from :
IPrincipal principal = Thread.CurrentPrincipal;
String user = principal.Identity.Name
  • And last if you really want to get name from HttContext.Current.User or Membership.GetUser() - because you ve got much existing code - you may write Custom Module for Authentication :

Look at my post : https://stackoverflow.com/a/33523683/5295797
And read carefully section Implementation of the module.
There is also the full project that can be downloaded from a link in that post.

Regards

Community
  • 1
  • 1
Emmanuel DURIN
  • 4,803
  • 2
  • 28
  • 53