I have a Silverlight 4 client that invokes several WCF services. We want the communication to be encrypted using SSL (I have this part solved already) and that every call be authenticated against AD LDS (ADAM), do you have any simple example showing how to make this work? There's plenty of documentation on the oh-so-many WCF options but I haven't been able to find a simple working example of this particular (but I think very common) scenario (SSL encryption + ADAM authentication + Silverlight). Any help or pointers greatly appreciated.
You can use CustomUserNameValidator in WCF: http://msdn.microsoft.com/en-us/library/aa702565.aspx http://nayyeri.net/custom-username-and-password-authentication-in-wcf-3-5
and in Custom Validator's Validate Method you can query ADAM to authenticate user.
Regards.
Try this link on the Codeplex website, it seems like the setup and configuration for the scenario you've described. It provides a thorough checklist of all of the required settings:
Intranet – Web to Remote WCF Using Transport Security (Trusted Subsystem, HTTP)
If this isn't you exact scenario, take a look at the following section that may fill the gaps:
I do not see any "add comment" option.
Hi, Edgar Sánchez!
Have you already looked for? Can you share how it went on?
The answer may depend on how you will handle permissioning, since you use ASP.net's membership provider for these functions.
If you want claims based authorization ADFS 1.0 (not 2.0) supports ADAM. If you want a STS that has more options try codplex's StarterSTS
If you want to use Role-Based Administration, try Enterprise Library from Microsoft P&P, the ASP.net membership provider, or direct COM access to Authorization manager (formerly known as AzMan)
I prefer & use the claims-based approach:
- Use ActiveDirectory + ADFS 2.0
(why not use the built in fault tolerance and replication of AD?, ADAM is a pain) - Silverlight Implementation of Ws-Trust as documented here: http://www.leastprivilege.com/UsingSilverlightToAccessWIFSecuredWCFServices.aspx
Edgar, I'm also interested in any results you had, I am at the same place you were in.
Shoaib, I have looked at this but I think it is less desirable than using just the .config via ActiveDirectoryMembershipProvider, as then you are just using off the shelf components, not writing your own security system.
EDIT: I hope this helps someone. I can't belive there is not a good example of this on the internet. It is simple enough. As I said before this is superior to using a custom authentication system.
Using AD LDS (ADAM) authentication with Silverlight compatible WCF calls (non wsHttp)
Client side:
1) Invocations from Silverlight look like this, this works if you are using the Channel Factory too.
var client = new MyWCFServiceClient();
client.GetPersonCompleted += client_GetPersonCompleted;
client.ClientCredentials.UserName.UserName = username;
client.ClientCredentials.UserName.Password = password;
client.GetPersonAsync();
2) return values from server will have an Error property if the login fails. If the user lookup fails, the error is something confusing like “at least one security token could not be validated”. Since your server side code is not able to re-wrap this (as it's all happening in the web.config) it is better for your client code to catch System.ServiceModel.Security.MessageSecurityException and interpret it as a login failure, or check the InnerException message to make sure it is the "security token" thing.
void client_GetPersonCompleted(object sender, GetPersonCompletedEventArgs e)
{
if (e.Error == null)
{
// do stuff with e.Result;
}
if (e.Error is MessageSecurityException)
{
// Your login did not work
}
}
Server side:
1) The WCF service class must have [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] or Required
2) you have to set up your LDS instance with SSL enabled, which is tricky. See: h't't'p://erlend.oftedal.no/blog/?blogid=7
3) web config - need to:
- Add the LDS connection string
- Add ActiveDirectoryMembershipProvider
- Add
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
- change your custom binding to include
<security authenticationMode="UserNameOverTransport"/>
see http://msdn.microsoft.com/en-us/library/dd833059(VS.95).aspx
Example:
<configuration>
<connectionStrings>
<add name="ADConnectionString" connectionString="LDAP://myserver:[SSL port number]/[where your user are in LDS, in my case: ‘OU=ADAM Users,O=Microsoft,C=US’]" />
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<membership defaultProvider="MyActiveDirectoryMembershipProvider">
<providers>
<add
name="MyActiveDirectoryMembershipProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="ADConnectionString"
connectionUsername="[domain]\[username]"
connectionPassword="[plain text windows password]"
connectionProtection="Secure"
/>
</providers>
</membership>
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehaviour">
<serviceMetadata
httpsGetEnabled="true"
httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceCredentials>
<userNameAuthentication
userNamePasswordValidationMode="MembershipProvider"
membershipProviderName="MyActiveDirectoryMembershipProvider"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="myCustomBinding">
<security authenticationMode="UserNameOverTransport"/>
<!-- <binaryMessageEncoding /> this is optional, but good for performance-->
<httpsTransport />
</binding>
</customBinding>
</bindings>
<services>
<service name="MessageBasedSecurity.Web.MyWCFService" behaviorConfiguration="MyServiceBehaviour">
<endpoint address="" binding="customBinding" bindingConfiguration="myCustomBinding"
contract="MessageBasedSecurity.Web.MyWCFService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
</configuration>
I hope this helps someone. I welcome comments/improvements.