I am working on adding WIF support to my WCF Data Services / ODATA server, and the first thing I'd like to do is create a nUnit test which passes some sort of identity to said server. I believe this falls under the category of an active client: there's no UI; I want to make a call out to a app.config established provider (Google, Yahoo, Windows Live, or some other provider) to get my identity token. Frankly, it doesn't matter what, just that it's more-or-less always accessable and has no administration to get the test running. (If there's some host app that I can include in my solution to act as an IP, I'd be perfectly happy with that.)
All of my existing tests use HttpRequest directly -- I am not using a generated client. While I'm creating my HttpRequest object, I check to see if I already have an authentication token to put in my headers. If not, I am trying something like this:
using (WSTrustChannelFactory factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(new Uri("https://dev.login.live.com/wstlogin.srf"))))
{
factory.Credentials.UserName.UserName = "MYUSERNAME";
factory.Credentials.UserName.Password = "MYPASSWORD";
factory.TrustVersion = TrustVersion.WSTrust13;
WSTrustChannel channel = null;
try
{
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointAddress("http://localhost:60711/Service"),
KeyType = WSTrust13Constants.KeyTypes.Bearer,
};
channel = (WSTrustChannel)factory.CreateChannel();
return channel.Issue(rst);
}
finally
{
if (null != channel)
{
channel.Abort();
}
factory.Abort();
}
}
So to start... I don't even know if I'm aiming at the right URI for the IP, but when I changed it, I got a 404, so I figure maybe I'm on the right track there. At the moment, the channel.Issue method returns a MessageSecurityException with an inner exception of type FaultException, noting "Invalid Request". The FaultException has a Code with Name=Sender and Namespace=http://www.w3.org/2003/05/soap-envelope, which then has a SubCode with Name=InvalidRequest and Namespace=http://schemas.xmlsoap.org/ws/2005/02/trust. I don't know what to do with this information. :)
My apologies if I'm asking a very basic question. I've been looking at authentication for only a couple of days, and don't know my way around yet. Thanks for any help!
EDIT -- SOLUTION
Eugenio is right -- I am doing something a little heavyweight, and it is more of integration testing stuff. I ditched the Google/Yahoo/Live stuff, and found a modified version of SelfSTS, which I cobbled into my project. I don't fully understand what's going on just yet, but I got back a SAML token. Here is final code:
var binding = new WS2007HttpBinding();
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.NegotiateServiceCredential = true;
using (var trustChannelFactory = new WSTrustChannelFactory(binding, new EndpointAddress(new Uri("http://localhost:8099/STS/Username"), new DnsEndpointIdentity("adventureWorks"), new AddressHeaderCollection())))
{
trustChannelFactory.Credentials.UserName.UserName = MYUSERNAME;
trustChannelFactory.Credentials.UserName.Password = MYPASSWORD;
trustChannelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
WSTrustChannel channel = null;
try
{
var rst = new RequestSecurityToken(WSTrust13Constants.RequestTypes.Issue)
{
AppliesTo = new EndpointAddress("http://localhost:60711/Service"),
KeyType = WSTrust13Constants.KeyTypes.Bearer
};
channel = (WSTrustChannel)trustChannelFactory.CreateChannel();
GenericXmlSecurityToken token = channel.Issue(rst) as GenericXmlSecurityToken;
((IChannel)channel).Close();
channel = null;
trustChannelFactory.Close();
string tokenString = token.TokenXml.OuterXml;
return tokenString;
}
finally
{
if (null != channel)
{
((IChannel)channel).Abort();
}
trustChannelFactory.Abort();
}
}
(This code is also lifted from the same place I got the modified SelfSTS -- thanks, Wade Wegner!)
I'm not sure about the use of "adventureWorks". The version of SelfSTS that I'm using names that as the issuername in the configuration, but I haven't checked to see if there's any correlation.
So... now to hack up my actual server so that it can figure out what to do with the SAML!
Not all of those IPs support active calls. Even if they do, the protocols might not be compatible.
For example, I'm not sure Google implements WS-Trust (what WIF is using under the hood). LiveID might have a WS-Trust endpoint somewhere, but not sure if it is officially supported/documented (for example, likely the error you are getting is because LiveID doesn't know about your RP: http:// localhost:60711/Service; and thus cannot issue a token for it).
For multiple IPs like these, apps typically embed a web browser and use it for all token negotiations (using WS-Federation for example). And often they rely on a specialized STS to deal with protocol transitions (e.g. Windows Azure Access Control Service)
In any case, what you are doing sounds a little bit heavyweight for a Unit test. It sounds more like an integration test you want to automate.
Maybe you could start with a custom (fake) STS of your own. In which you can control everything and simulate different outputs (e.g. different claims, etc.)
This chapter: http://msdn.microsoft.com/en-us/library/hh446528 (and the samples) can give you more information.