We're working on a WCF service to house our core API.
We're working on 2 clients to use this API one of which is a WPF desktop app that will most likely authenticate against active directory and reside on the same domain as the API. The other is an ASP.Net web application that will most likely use ASP.Net Membership for security and still reside on the same domain as the WCF service. The plan is for the WCF service to use NetTcp and be hosted inside a Windows Service.
Where possible I would like the WCF service to run as the calling user, I guess this should be fairly straight forward for the desktop app where the user is a domain user. For the web app I guess I will need to create a user for service calls to run under.
Is it possible to get this kind of double approach to security to run over a single WCF service or will I need to make 2 services each with it's own security model?
Also if anyone has any thoughts on best practises/patterns for achieving this that would be great.
Thanks
I've solved the same problem in the following way:
I created two net.tcp binding for every usage.
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="" />
<message clientCredentialType="UserName" />
</security>
</binding>
<binding name="WindowsBinding" >
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Windows" />
</security>
</binding>
Next, I added two endpoints for every services
<service name="SimplePluginService" behaviorConfiguration="CommonBehavior">
<endpoint binding="netTcpBinding" bindingConfiguration="UserNameBinding" name="SimplePluginServiceUserName" contract="ISimplePluginService">
<identity>
<dns value="WCfServer" />
</identity>
</endpoint>
<endpoint binding="netTcpBinding" bindingConfiguration="WindowsBinding" name="SimplePluginServiceWindows" contract="ISimplePluginService">
<identity>
<dns value="WCfServer" />
</identity>
</endpoint>
</service>
Next, I choosed the appropriate endpoint when I create ChannelFactory (ConnectionManager - class, which contains information about user's creditionals).
private readonly Dictionary<Type, Object> channelFactoryDictionary = new Dictionary<Type, Object>();
private ChannelFactory<T> GetChannelFactory<T>() where T : class
{
if (channelFactoryDictionary.Keys.Contains(typeof(T)))
{
return channelFactoryDictionary[typeof(T)] as ChannelFactory<T>;
}
else
{
string endpointName=typeof(T).ToString();
if (ConnectionManager.IsWindowsAuth) endpointName+="Windows";
else endpointName+="UserName";
ChannelFactory<T> channelFactory = new ChannelFactory<T>(endpointName);
if (!ConnectionManager.IsWindowsAuth){
channelFactory.Credentials.UserName.UserName = ConnectionManager.Password;
channelFactory.Credentials.UserName.Password = ConnectionManager.Password;
}
channelFactoryDictionary.Add(typeof(T), channelFactory);
return channelFactory;
}
}
You're conflating a few different issues here. First, I'd claim that your hosting (Windows service) and transport (here given as netTcp) are mostly unrelated to your choice of authentication/authorization, unless you want to rely on ASP.NET membership, in which case you'll want IIS to host your service.
Impersonation is certainly a decent approach to access control on a desktop or in a carefully-curated LDAP environment; I've tended to find, though, that as my services grow their business requirements deviate from policies that are (or can be) represented in Active Directory. Once that happens, your service begins to require more support to keep Windows impersonation working as you'd wish. This often means dealing with WCF extensibility.
Speaking from years of experience, working with WCF security extension points is roughly as painful as a barely-anesthetized root canal. That said, it's quite powerful once the painful work is completed.
I've found that, generally, less pain is better. I can get the security benefits of impersonation by explicitly modeling access restrictions in my code, e.g. using claims-based security and CAS attributes. Taking that approach, a custom IPrincipal-implementing class can obviate the need for impersonation and, with it, much obscure WCF plumbing.
It isn't quite the answer you were looking for, but these are my two cents' worth nevertheless.
Related
In VS2013 I have added Service Reference, not Web Reference, to existing legacy ASMX web service(s).
Does this somehow give this reference the ability to implement WCF features like Reliable Messaging, or any other new features of WCF?
I have done some reading and I think that at least for Reliable Messaging we do not magically get this ability, since as I understand it this feature has to be both enabled on the 'client' and 'server' side, ie both endpoints, and of course the old ASMX web services, the server in this case, don't support Reliable Messaging standard, even if I were to somehow configure the 'client' to do so.
I have been told by resident developers to add these old ASMX web services as a Service Reference as it gives the ability to do async calls and 'some' other configuration benefits, but I am not too sure about this from what I have now read.
What you need is an adapter pattern. The way I am looking at it is more from design patterns perspective rather than WCF.
[ServiceContract]
public interface IService
{
[OperationContract]
string SayHello(string toWhom);
}
public class Service : IService
{
public string SayHello(string toWhom)
{
// consume the ASMX service here and return the result.
}
}
in the binding you can use the features that are missing in ASMX.
<bindings>
<wsHttpBinding>
<binding>
<reliableSession/>
<security>
<message />
</security>
</binding>
</wsHttpBinding>
</bindings>
Is it possible to create a WCF REST 4.0 service which has two endpoints with different behaviours? Specifically I’m looking for one endpoint to use transferMode=Streamed, and the other to use Buffered.
I’ve started off using the WCF REST Service Application, which seems to be a mix of WCF and ASP MVC routing technologies. I can set transferMode for both endpoints in
system.serviceModel/standardEndpoints/webHttpEndpoint/standardEndpoint
but am not having any joy applying additional ones to my routes.
I’m not quite clear where the WCF/MVC separation exists, for example whether the Global.asax counts as one WCF endpoint or whether the routes are individual endpoints, and as a result am not sure how to progress.
Is there a simple Web.config change or attribute I can apply to a service to specify different behaviour?
If not, can I create separate asax files using different behaviours instead of one Global.asax file?
If not, should I create .svc files to map onto my classes as in a normal WCF application?
If not, will I have to create a second project to define different behaviour?
Each service route creates a new ServiceHost. Also, REST Starter Kit is now deprecated, either you should use straight WCF REST 4.0, or you should look into the new WCF Web API stuff at http://wcf.codeplex.com
What about doing it that way:
<services>
<service name="YourNamespace.YourServiceClass">
<endpoint address="stream" kind="webHttpEndpoint" endpointConfiguration="webHttpStreamed" contract="YouServiceContract" />
<endpoint address="buff" kind="webHttpEndpoint" endpointConfiguration="webHttpBuffered" contract="YouServiceContract" />
</service>
</services>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="webHttpStreamed" transferMode="Streamed" />
<standardEndpoint name="webHttpBuffered" transferMode="Buffered" />
</webHttpEndpoint>
</standardEndpoints>
Of course, the two endpoints' addresses must not be overlapping.
I am trying to find some good tutorials that would show me to create a simple SOAP based service using WCF and deploy it. I have been googling for the past 2 hour and can't seem to find any good resource.. Can anyone help me?
As for resources: there's the MSDN WCF Developer Center which has everything from beginner's tutorials to articles and sample code.
Also, check out the screen cast library up on MSDN for some really useful, 10-15 minute chunks of information on just about any topic related to WCF you might be interested in.
Also very good are The Service Station articles in MSDN magazine on various aspects of WCF - some more basic like Serialization in WCF or WCF Bindings in Depth, some more advanced and esoteric - but always worth a look!
Update: for learning WCF and SOAP, check out e.g.
Getting Started Tutorial
First Steps Screencast about WCF (using SOAP!)
and a great many more - there are a ton of tutorial and learnings materials on WCF using SOAP bindings - not just REST stuff for sure!
REST / SOAP endpoints for a WCF service
You can expose the service in two different endpoints. the SOAP one can use the binding that support SOAP e.g. basicHttpBinding, the RESTful one can use the webHttpBinding. I assume your REST service will be in JSON, in that case, you need to configure the two endpoints with the following behaviour configuration
<endpointBehaviors>
<behavior name="jsonBehavior">
<enableWebScript/>
</behavior>
</endpointBehaviors>
An example of endpoint configuration in your scenario is
<services>
<service name="TestService">
<endpoint address="soap" binding="basicHttpBinding" contract="ITestService"/>
<endpoint address="json" binding="webHttpBinding" behaviorConfiguration="jsonBehavior" contract="ITestService"/>
</service>
</services>
so, the service will be available at
http://www.example.com/soap
http://www.example.com/json
Apply [WebGet] to the operation contract to make it RESTful. e.g.
public interface ITestService
{
[OperationContract]
[WebGet]
string HelloWorld(string text)
}
Note, if the REST service is not in JSON, parameters of the operations can not contain complex type.
For plain old XML as return format, this is an example that would work both for SOAP and XML.
[ServiceContract(Namespace = "http://test")]
public interface ITestService
{
[OperationContract]
[WebGet(UriTemplate = "accounts/{id}")]
Account[] GetAccount(string id);
}
POX behavior for REST Plain Old XML
<behavior name="poxBehavior">
<webHttp/>
</behavior>
Endpoints
<services>
<service name="TestService">
<endpoint address="soap" binding="basicHttpBinding" contract="ITestService"/>
<endpoint address="xml" binding="webHttpBinding" behaviorConfiguration="poxBehavior" contract="ITestService"/>
</service>
</services>
Service will be available at
http://www.example.com/soap
http://www.example.com/xml
REST request try it in browser,
http://www.example.com/xml/accounts/A123
SOAP request client endpoint configuration for SOAP service after adding the service reference,
<client>
<endpoint address="http://www.example.com/soap" binding="basicHttpBinding"
contract="ITestService" name="BasicHttpBinding_ITestService" />
in C#
TestServiceClient client = new TestServiceClient();
client.GetAccount("A123");
Another way of doing it is to expose two different service contract and each one with specific configuration. This may generate some duplicates at code level, however at the end of the day, you want to make it working.
WCF is a technology for building services. It does not assume that the services are SOAP services or RESTFul or anything else. You have to learn WCf basics such as Service and DataContracts, Endpoints, Bindings etc to be able to work with any kind of service.
The links given marc_s are very helpful for that.
Now as far as SOAP is concerned, it is a format\technology used to transport messages from one endpoint to another. This details is covered by the Binding aspect of the WCF. When you expose and consume services you just have to choose a Binding which uses SOAP.
Hence, you should, using links given by marc_s, learn WCF basics to build a service. Then you will know how to build a service and which binding to choose to use SOAP.
Hope this helps.
I've got a WCF service that will need to receive client credentials, and maintain some kind of role-based data, based on my auth method.
The clients will reside on many different systems, and as such, each client will have a unique userID and pw.
I'm using basicHttpBinding and have read a few articles, such as this one,
http://nirajrules.wordpress.com/2009/05/22/username-over-https-custombinding-with-wcf%E2%80%99s-channelfactory-interface/, that describe the process.
So what I'm looking for is if someone has a full client/server configured like this to take a look at so I can derive my own solution from this.
What I'd like to do is have the username and password passed in the headers for each request, passing back some kind of SecurityTokenValidationException on fail, or continuing if passing.
Thanks.
UPDATE
I'm using the wsHttpbinding with the following config on both the client and server:
<wsHttpBinding>
<binding name="wsHttpEndpointBinding" >
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Basic" />
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
And the call out to the server from the client as follows:
ServiceReference1.ServiceClient myClient = new ServiceReference1.ServiceClient();
myClient.ClientCredentials.UserName.UserName = "billuser";
myClient.ClientCredentials.UserName.Password = "mypassword";
Response.Write("Data from WCF Service: " + myClient.GetData(1));
I think I need a bit of a hand with linking up the CustomUsernamePasswordValidator on the server as I'm still getting the '...could not be activated.' error.
Are you required to use the basicHttpBinding? That binding is really only there to provide support for legacy WS-BasicProfile implementations (i.e. ASMX). If your clients are also .NET/WCF, I would highly recommend using wsHttpBinding, which provides a plethora of security options out of the box. You could use certificates, username/password, etc. with transport and/or message security and not need to write any of the security stuff yourself. Just configure-and-go (CAG).
Security credential information is available to the service itself via the OperationContext, in case you need to access it directly from your code. If your code does need to access it, however, I would recommend writing a behavior to extract the pertinent information from the OperationContext and place it in something more application specific so that you don't have to reference System.ServiceModel everywhere you need to access information on OperationContext.
My question is in regards to the best (aka "least painful") way to secure access to a WCF service that is only exposed to our company's internal users. The goal is to ensure that the service is only accessed via a single Windows forms application that each of our users has installed. When the service is called, I want the service to be able to validate that it was called from the permitted application.
The service to be secured uses basicHttpBinding, which supports streaming, so I believe I am limited to Transport level security.
Below are simplified versions of the <bindings> and <services> sections from my service's config file.
<bindings>
<basicHttpBinding>
<binding name="Service1Binding" transferMode="Streamed"/>
</basicHttpBinding>
</bindings>
<services>
<service name="WCFServiceSecurity.Service1"
behaviorConfiguration="WCFServiceSecurity.Service1Behavior">
<endpoint address=""
binding="basicHttpBinding"
contract="WCFServiceSecurity.IService1"
bindingConfiguration="Service1Binding"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
Can anyone offer some details as to what actions I would need to take in order to implement security on this service?
Note: I'm new to WCF and am not familiar with security at all, so let me know if I haven't provided enough detail.
UPDATE:
As suggested by marc_s, I'd like to secure the WCF service using some sort of username/password mechanism. This gives a little more direction towards an answer, but I'm still somewhat blurry on how to actually do this.
Because my service requires streaming to be enabled, I have to use basicHttpBinding and Transport level security (right?); further to that, the method contained in my service can only accept a Stream object.
Taking those constraints into consideration along with my preference to use username/password validation...
How should I modify my service's config file to force username/password credentials to be supplied?
How will my service validate the supplied credentials?
How will my client application pass credentials the service when making a call?
Will this require using SSL and, if so, will all client machines require a certificate as well?
UPDATE:
After explaining the trouble I've been having with securing this service to my boss, I was given the go-ahead to try the Windows Authentication route. Sadly, I've had no luck in implementing this type of authentication with my Streamed service (argh). After making the appropriate changes (as outlined here - the only exception being that my transferMode="Streamed") and accessing my service, I was presented with the following error:
HTTP request streaming cannot be used in conjunction with HTTP authentication. Either disable request streaming or specify anonymous HTTP authentication.
I then stumbled upon the following quote here which offers some clarification:
You can't do transport auth. with streaming. If you have to use HTTP request streaming, you'll have to run without security.
The way security works is:
WCF Client makes an http request to the Server.
The Server responds with something saying, "You aren't authorized, send me a basic/digest/etc credential."
The Client gets that response and resends its message with the credentials tacked on.
Now the Server gets the message, verifies the credentials, and continues.
Request Streaming isn't designed to work with that security pattern. If it did, it would be really slow, since the Client would send the entire stream, get the message from the Server that it wasn't authorized, then it would have to resend the entire stream with credentials.
So now I'm looking for opinions, how would you secure your streaming-enabled WCF service? As mentioned previously, some sort of username/password mechanism would be preferred. Feel free to think outside the box on this one...
Any help is greatly appreciated!
Well, I found a lot of issues surrounding security/streaming while working on this problem. The hack (er...um...workaround) I finally ended up going with was to create a new DataContract that inherits MemoryStream and decorated it with a BaseStream property (for holding the data I want streamed) along with appropriate properties used for simple authentication.
Here is the resulting DataContract:
[DataContract]
[KnownType( typeof( MemoryStream ) )]
public class StreamWithCredentials : MemoryStream
{
[DataMember]
public Stream BaseStream { get; set; }
[DataMember]
public string Username { get; set; }
[DataMember]
public string Password { get; set; }
}
The above DataContract ends up being the input parameter of my service's method. The first action my service takes is to authenticate the supplied credentials against known valid values and to continue as appropriate.
Now I do know that this is not the most secure option but my directive was to avoid using SSL (which I'm not even sure is possible anyway - as stated here) for this internal process.
That being said, this was the best solution to the above stated problem I could come up with, hope this helps anyone else stricken with this issue.
Thanks to all who responded.
There's a number of things you could do:
add a certificate to each and every machine that's allowed to use your service, and check for that certificate. That only allows you to exclude "unauthorized" machines - you cannot limit it to a specific application
same as above, but include the certificate embedded in your winforms app and send it from there (do not store it in the machine's certificate store)
require a username / password that only that particular app of yours knows about and can transmit to your service; e.g. someone else would not be able to present the appropriate credentials
EDIT 2: OK, so the username/password approach seems to get out of hand.... what if you just have basic transport security (SSL) for basic protection, and then use the MessageContract to define header and body of your SOAP message, include a specific value in the header, and then just check for that presence of the element in the header in your service?
Something like that:
[DataContract]
class YourRequestData
{
...
}
[MessageContract]
public class YourRequest
{
[MessageBodyMember]
public YourRequestData bodyData { get; set; }
[MessageHeader]
public string AppThumbprint { get; set; }
}
And then on your server in your code just check for the presence and the validity of that AppThumbprint code:
public Stream RequestStream(YourRequest request)
{
if(AppThumbprintIsValid(request.AppThumbprint))
{
.... begin your streaming
}
}
That might end up being a lot easier than the username/password security scenario.
Marc
Please correct me if I am wrong, but:
if you are using forms authentication for your WCf service (on asp.net), just add a login method to your service, in it you create the required cookie (formsAuthentication.Authenticate()). which is automatically sent with the response, the client can then call the stream API without needing extra parameters (a requirement for it to be STREAM) and you can check the identity in the streaming api before you fire off the returning stream.
As for securing access to the whole WCF, I get the feeling that embedding a certificate in the .net app is one way to go. they would have to ildump your app to get at it.
you can tell asp.net/wcf not to provide the wsdl, or more accurately, to not automatically generate the wsdl. Without wsdl access it gets much harder for them to generate a proxy....
If you want to use basicHttpBinding (for interop) you can only pass your credential at the message level. You have to set your security configuration to TransportWithMessageCredential.
To do that you have to create a SSL channel, so you need a certificate at server side, and it's not necesary for a cliente to have one.
It is possible to use Windows authentication with Streaming and SSL, but you must use TransportWithMessageCredential:
<basicHttpBinding>
<binding name="FileService.FileServiceBinding" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" transferMode="Streamed">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
You need to set in code proxy.ClientCredentials.UserName.UserName and proxy.ClientCredentials.UserName.Password.
If this is going to be an application that lives on the intranet it might be easiest to just create a new group in your Active Directory and only give members of that group the ability to use the service.
You can add Authentication (using windows credentials) with something like this:
<basicHttpBinding>
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</basicHttpBinding>
Could then Authorise by decorating the Interface to your services methods:
<PrincipalPermission(SecurityAction.Demand, Role:="MyAppsUsers")> _
Public Function MyMethod() As String Implements IService.MyMethod
Heres a good link to Security in WCF. It has lots of How To's at the end (the one titled 'How To - Use basicHttpBinding with Windows Authentication and TransportCreditals' might be of use to you).
Wcf Secruity
[Disclaimer: I'm also new to WCF and haven’t done this exact case before so apologises if this is slightly off!]