I have an ASP.NET MVC web app that uses ADFS 2.0 for authentication. Some of the MVC controller actions function as generic web service endpoints, receiving and serving JSON. I want to build a client application that automates some of the app's functionality. For that purpose, I am building an API access library that will make HTTP requests to the web service in order to do its job.
I am stuck at trying to authenticate. I am using forms authentication for ADFS 2.0, so shouldn't I be able to simply simulate the form post with a valid username and password in order to generate a token? Instead of receiving a token back, I just get the login page. I am not sure what else I need to do in order to authenticate my request. My code is pasted below... but maybe I'm doing it totally wrong and there's something I don't know about?
string postData = string.Empty;
postData += "ctl00$ContentPlaceHolder1$UsernameTextBox=" + username + "&";
postData += "ctl00$ContentPlaceHolder1$PasswordTextBox=" + password;
postData += "&AuthMethod=FormsAuthentication";// Submit the data back
string url = "{url of website}";
HttpWebRequest getTokenRequest = WebRequest.Create(url) as HttpWebRequest;
getTokenRequest.CookieContainer = cookies;
getTokenRequest.ContentType = "application/x-www-form-urlencoded";
getTokenRequest.ContentLength = postData.Length;
getTokenRequest.Method = "POST";
// post the data to the request
using (StreamWriter sw = new StreamWriter(getTokenRequest.GetRequestStream()))
{
sw.Write(postData);
sw.Flush();
sw.Close();
}
HttpWebResponse getTokenResponse = (HttpWebResponse)getTokenRequest.GetResponse();
string responseString = ResponseToString(getTokenResponse);
I have also tried another approach, which also doesn't work. This uses WCF. I get the error:
Secure channel cannot be opened because security negotiation with the remote endpoint has failed.This may be due to absent or incorrectly specified EndpointIdentity in the EndpointAddress used to create the channel. Please verify the EndpointIdentity specified or implied by the EndpointAddress correctly identifies the remote endpoint.
const string relyingPartyId = "[ID]"; //ID of the relying party in AD FS
const string adfsEndpoint = "https://[server]/adfs/services/trust/13/usernamemixed"; //url to hit - username & pw?
const string certSubject = "[subject]"; //?
//Setup the connection to ADFS
var factory = new Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory(
new WindowsWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(adfsEndpoint));
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.UserName.UserName = "[un]";
factory.Credentials.UserName.Password = "[pw]";
//Setup the request object
var rst = new Microsoft.IdentityModel.Protocols.WSTrust.RequestSecurityToken
{
RequestType = Microsoft.IdentityModel.SecurityTokenService.RequestTypes.Issue,
KeyType = Microsoft.IdentityModel.SecurityTokenService.KeyTypes.Bearer,
AppliesTo = new EndpointAddress(relyingPartyId)
};
//Open a connection to ADFS and get a token for the logged in user
var channel = factory.CreateChannel();
//added to solve a trust certificate issue - bad from a security perspective
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
(se, cert, chain, sslerror) =>
{
return true;
};
var genericToken = channel.Issue(rst) as GenericXmlSecurityToken;
Most likely the problem is in:
string url = "{url of website}";
. And/or missing parameters in the POST.It shouldn't be just any URL. It should be a properly formatted WS-Federation request. With timestamps etc. etc. And normally/sometimes (in current ADFS) it is a two step process. First the normal request, then the real (uid+pwd) authentication. Both with proper WS-Fed parameters in the proper places. Only uid and pwd is not enough.
Now the obvious reply, no insult intended: I suggest you make a trace of the regular logon process and then compare those exact HTTP requests with your requests.