none
Using one Service for Multiple Clients RRS feed

  • Question

  • I have set up a WCF Service with multiple endpoints. The intention being to use the WCF to serve a database. One user has one log in etc.

    So after some research I decorate the .svc like so:

    <ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerCall, AutomaticSessionShutdown:=True,
                     ConcurrencyMode:=ConcurrencyMode.Single, IncludeExceptionDetailInFaults:=True)>

    I made thought that with the InstanceContextMode being Per Call, that it would create a Service Instance Per Call, meaning it would fire the UsernameAuthentication after each call... However after putting in a line to write to the event log, this doesn't happen.

    Validation:

    Imports System.Data.SqlClient
    Imports System.IdentityModel.Selectors
    Imports System.Reflection
    Imports System.Security
    Imports System.Threading
    Imports DVPWCFService.Modules
    
    Namespace Classes.Admin
    
        Public Class Validation
            Inherits UserNamePasswordValidator
    
            Private _dbUsername As String
            Private _dbPassword As SecureString
    
            Public Overrides Sub Validate(userName As String, password As String)
                Try
                    _dbUsername = userName
                    _dbPassword = ConvertToSecureString(password)
                    DbUsername = userName
                    DbPassword = ConvertToSecureString(password)
    
                    Dim dummyConnectionForValidation As SqlConnection = LogInTry("A real server name")
    
                    dummyConnectionForValidation.Close()
                    dummyConnectionForValidation.Dispose()
    
                Catch ex As Exception
    
                    If ex.GetType Is GetType(SqlException) Then
                        DVPEventLog.WriteEntry("Authentication has failed for user: '" + userName + "'", EventLogEntryType.Warning)
                    Else
                        DVPEventLog.WriteEntry("Error: " & ex.Message & Environment.NewLine & Environment.NewLine & "Stack Trace: " & ex.StackTrace & Environment.NewLine & Environment.NewLine & "Method Name: " & MethodBase.GetCurrentMethod.Name, EventLogEntryType.Error, EventLogEntryType.Error)
                    End If
    
                    Thread.Sleep(5000)
    
                    Throw New FaultException("Log in failed.")
                End Try
            End Sub
    
            Private Function LogInTry(serverName As String) As SqlConnection
                LogInTry = New SqlConnection
                LogInTry.ConnectionString = "Data Source=" & serverName & ";Persist Security Info=True;MultipleActiveResultSets=True;"
                LogInTry.FireInfoMessageEventOnUserErrors = True
                LogInTry.Credential = New SqlCredential(_dbUsername, _dbPassword)
                LogInTry.Open()
            End Function
    
            Private Function ConvertToSecureString(convertee As String) As SecureString
    
                ConvertToSecureString = New SecureString()
    
                For Each stringCharacter In convertee.ToCharArray()
                    ConvertToSecureString.AppendChar(stringCharacter)
                Next
    
                ConvertToSecureString.MakeReadOnly()
    
            End Function
    
        End Class
    End Namespace

    So in the validation code I just make sure the user can log in to the server. I store the username so I can use it again after the Validation occurs because i presumed after the validation it'd go straight to the call I made without disposing of anything.

    App Config:

    <?xml version="1.0"?>
    <configuration>
    
      <appSettings>
        <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
      </appSettings>
      <system.web>
        <compilation debug="true" strict="false" explicit="true" targetFramework="4.5.2" />
        <httpRuntime targetFramework="4.5.2"/>
      </system.web>
      <system.serviceModel>
        <bindings>
    
          <wsHttpBinding>
            <binding name="SSL Binding" openTimeout="00:00:20" receiveTimeout="08:00:00" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647">
              <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="2147483647"
                maxBytesPerRead="4096" maxNameTableCharCount="2147483647" />
              <security mode="TransportWithMessageCredential">
                <transport clientCredentialType="None" />
                <message clientCredentialType="UserName" algorithmSuite="TripleDesSha256Rsa15" />
              </security>
            </binding>
          </wsHttpBinding>
    
        </bindings>
        <services>
    
          <service behaviorConfiguration="Custom Validation Service Behavior" name="DVPWCFService.DVP">
            <endpoint address="" binding="wsHttpBinding" bindingConfiguration="SSL Binding"
              name="IComponent" contract="DVPWCFService.Interfaces.IComponent" />
            <endpoint address="" binding="wsHttpBinding" bindingConfiguration="SSL Binding"
              name="IProgramme" contract="DVPWCFService.Interfaces.IProgramme" />
            <endpoint address="" binding="wsHttpBinding" bindingConfiguration="SSL Binding"
              name="IVehicle" contract="DVPWCFService.Interfaces.IVehicle" />
            <endpoint address="" binding="wsHttpBinding" bindingConfiguration="SSL Binding"
              name="IMiscellaneous" contract="DVPWCFService.Interfaces.IMiscellaneous" />
            <endpoint address="mex" binding="mexHttpsBinding" name="metadata"
              contract="IMetadataExchange" />
            <endpoint address="" binding="wsHttpBinding" bindingConfiguration="SSL Binding"
              name="IReservation" contract="DVPWCFService.Interfaces.IReservation" />
          </service>
    
        </services>
        <behaviors>
          <serviceBehaviors>
    
            <behavior name="Custom Validation Service Behavior">
              <serviceMetadata httpsGetEnabled="true" />
              <serviceDebug httpHelpPageEnabled="true" httpsHelpPageEnabled="true"
                includeExceptionDetailInFaults="true" />
              <serviceCredentials>
                <serviceCertificate findValue="blahblahblah"
                  x509FindType="FindByThumbprint" />
                <userNameAuthentication userNamePasswordValidationMode="Custom"
                  customUserNamePasswordValidatorType="DVPWCFService.Classes.Admin.Validation, DVPWCFService"/>
              </serviceCredentials>
              <dataContractSerializer />
              <serviceThrottling maxConcurrentCalls="10" maxConcurrentSessions="10" maxConcurrentInstances="1" />
            </behavior>
    
          </serviceBehaviors>
        </behaviors>
        <protocolMapping>
          <add binding="wsHttpBinding" scheme="https" />
        </protocolMapping>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="false" />
      </system.serviceModel>
      <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
        <directoryBrowse enabled="false"/>
      </system.webServer>
    
    </configuration>

    This all falls apart when there's more than one person using the Service because it overwrites the Username and Password variables. This is strange as I set it to per call and single? Am I not disposing of it properly? Is there a way to make sure it gets disposed of and to make each call go through the validation etc?

    Tuesday, November 22, 2016 8:28 AM

All replies

  • The intention being to use the WCF to serve a database. One user has one log in etc.

    Most would just use a generic user-id and psw on the connectionstring so no user would need to be authenticated at the DB level. Any user authentication should be implemented on the client side well before backend code is even called. 

    Tuesday, November 22, 2016 6:10 PM
  • Hi Connor,

    >> However after putting in a line to write to the event log, this doesn't happen.

    Do you mean Validation did not fire for each call? I made a test with this, and I could reproduce your issue, it seems that the Validation will fire once for different calls in the same service client.

    >> Is there a way to make sure it gets disposed of and to make each call go through the validation etc?

    Could you share us the reason why you want to validate for each call? As my test, for customclient.ClientCredentials.UserName.UserName and Password, it could be set once for each Service client. If you set more than once, you will get error indicate that this is read-only. So, I think if the first call is authenticated for the client, all of the rest calls from this client will be authenticated.

    Could you share us how you pass different username password for the same service client?

    For another way to validate the username and password, I think you could try IAuthorizationPolicy implementations. By this way, you could get the username and password in each WCF method, and then you could validate the username and password in each WCF method. You could refer the link below for more information.

    # WCF Security - Getting the password of the user

    http://www.neovolve.com/2008/04/07/wcf-security-getting-the-password-of-the-user/

    Best Regards,

    Edward

    Note: This response contains a reference to a third party World Wide Web site. Microsoft is providing this information as a convenience to you.
    Microsoft does not control these sites and has not tested any software or information found on these sites;
    Therefore, Microsoft cannot make any representations regarding the quality, safety, or suitability of any software or information found there.
    There are inherent dangers in the use of any software found on the Internet, and Microsoft cautions you to make sure that you completely understand the risk before retrieving any software from the Internet.


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, November 23, 2016 5:09 AM