简体   繁体   中英

Authenticate user from WPF app across two web services (from WCF to WebApi)

I have the following situation:

WPF client application -> WCF Service -> WebApi Service

I want to be able to authenticate the user of the client application when in the WebApi service. I do not require full impersonation/delegation - all I require is the client's login ID, as I am using the user ID for a third party authorisation system. Both WCF Service and Web Api Service are on the same server.

The current approach I am trying is Impersonation of the WindowsIdentity, as follows.

1. WebApi service self-hosted with Owin

    WebApp.Start(options,
    builder =>
    {
        // Authenticate POST requests, but not GET requests
        // Adapted from: http://stackoverflow.com/questions/17457382/windows-authentication-with-signalr-and-owin-self-hosting
        var listener = (HttpListener)builder.Properties[typeof(HttpListener).FullName];
        listener.AuthenticationSchemes = 
            AuthenticationSchemes.Negotiate | AuthenticationSchemes.Anonymous;
        listener.AuthenticationSchemeSelectorDelegate =
            request => (request.HttpMethod == HttpMethod.Get.Method)
                ? AuthenticationSchemes.Anonymous
                : AuthenticationSchemes.Negotiate;

        // etc
    });

2. WCF Service

In <system.web> : <authentication mode="Windows" />

In <bindings> :

<binding name="BasicHttpEndpointBinding">
  <security mode="TransportCredentialOnly">
    <transport clientCredentialType="Windows" />
  </security>
</binding>

In the code for the specific function I want to impersonate:

WindowsIdentity callerIdentity = ServiceSecurityContext.Current.WindowsIdentity; 

using (callerIdentity.Impersonate())
{
    var request = ...;
    request.ImpersonationLevel = TokenImpersonationLevel.Impersonation;
    request.UseDefaultCredentials = true;

    // Use request to call Web Api
}

3. In the client:

<endpointBehaviors>
  <behavior name="ImpersonationBehavior">
    <clientCredentials>
      <windows allowedImpersonationLevel="Impersonation" />
    </clientCredentials>
  </behavior>
</endpointBehaviors>

With this set-up, all on my local PC, I see an error from the WCF service trying to connect to the WebApi service:

{"An attempt was made to access a socket in a way forbidden by its access permissions 127.0.0.1:9102"}

(Note that using WindowsIdentity callerIdentity = WindowsIdentity.GetCurrent(); instead of WindowsIdentity callerIdentity = ServiceSecurityContext.Current.WindowsIdentity; works fine - although obviously this is using the WCF user rather than then client app user)

My question - is this the most sensible way of determining the user name in the Web Api service? Is it even possible like this? If so, any idea how to debug the "forbidden" error and get this set-up working?

For anyone interested, I managed to get this working by changing the following.

  1. Run both services as Administrator. (I believe that with the right configuration, this wouldn't be required - but this is good enough for my purposes)

  2. Ensure the port reservation URL exactly matches the URL specified in the WebApi start-up parameters (See: Self hosted OWIN and urlacl ). In my case I used the following for WebApp.Start(...) parameter:

     string baseAddress = "http://+:9100"; StartOptions options = new StartOptions(); options.Urls.Add(baseAddress); 

And the following for the netsh:

netsh http add urlacl url=http://+:9111/ user=EVERYONE
  1. In the WCF service, the following behaviour was added to the method in the service implementation that I wanted to use impersonation:

     [OperationBehavior(Impersonation = ImpersonationOption.Required)] 

...and for other methods in this contract, I needed to add the following

    [OperationBehavior(Impersonation = ImpersonationOption.NotAllowed)]
  1. I referenced the WebApi service from the WCF service using "localhost" rather than an explicit hostname. Not sure whether this is required.

  2. The user running the WCF service needs to have permissions to use Impersonation (should already be set up if running as Administrator). Use gpedit.msc; navigate to Local Computer Policy -> Computer Configuration -> Windows Settings -> Security Settings -> Local Policies -> User Rights Assignment; ensure "Impersonate a client after authentication" contains the user that you run the WCF service as. (Taken from: https://blogs.technet.microsoft.com/askperf/2007/10/16/wmi-troubleshooting-impersonation-rights/ )

The final step would be to work out the minimal set of configuration required from both the question / this answer ... I think I'll leave that for another day :-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM