If you look at that response carefully, you'll see that the JavaScript actually makes a request of https://xxx.my.salesforce.com/, not your server. You need to follow one step further to see the request to your server. Here's an abbreviated version of what I just saw.
First, the initial OAuth request to a My Domain URL:
$ curl -v 'https://superpat-developer-edition.my.salesforce.com/services/oauth2/authorize?response_type=code&redirect_uri=...&client_id=...'
* ...
< HTTP/1.1 302 Found
< Location: https://superpat-developer-edition.my.salesforce.com/setup/secur/RemoteAccessAuthorizationPage.apexp?source=...
Following the redirect:
$ curl -v https://superpat-developer-edition.my.salesforce.com/setup/secur/RemoteAccessAuthorizationPage.apexp?source=...
* ...
< HTTP/1.1 200 OK
< ...
<
<script>
var escapedHash = '';
var url = '/saml/authn-request.jsp?saml_request_id=...&saml_acs=...&saml_binding_type=HttpPost&Issuer=https%3A%2F%2Fsuperpat-developer-edition.my.salesforce.com&urlSource=1&RelayState=%2Fsetup%2Fsecur%2FRemoteAccessAuthorizationPage.apexp%3Fsource%3D...';
if (window.location.hash) {
escapedHash = '%23' + window.location.hash.slice(1);
}
if (window.location.replace){
window.location.replace(url + escapedHash);
} else {;
window.location.href = url + escapedHash;
}
</script>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
...
</html>
Note - no server on that /saml/authn-request.jsp URL, so the request will go to https://superpat-developer-edition.my.salesforce.com/. Let's simulate it with curl
and see what happens:
$ curl -v 'https://superpat-developer-edition.my.salesforce.com/saml/authn-request.jsp?saml_request_id=...&saml_acs=...&saml_binding_type=HttpPost&Issuer=https%3A%2F%2Fsuperpat-developer-edition.my.salesforce.com&urlSource=1&RelayState=%2Fsetup%2Fsecur%2FRemoteAccessAuthorizationPage.apexp%3Fsource%3D...'
* ...
< HTTP/1.1 200 OK
< ...
<
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<body onload="document.forms[0].submit()">
<noscript>
<p>
<strong>Note:</strong> Since your browser does not support JavaScript,
you must press the Continue button once to proceed.
</p>
</noscript>
<form action="https://ADFS.example.com/adfs/ls/" method="post">
<div>
<input type="hidden" name="RelayState" value="/setup/secur/RemoteAccessAuthorizationPage.apexp?source=..."/>
<input type="hidden" name="SAMLRequest" value="..."/>
</div>
<noscript>
<div>
<input type="submit" value="Continue"/>
</div>
</noscript>
</form>
</body>
</html>
And there's the request that's posted to the SAML endpoint you configured (in my case, https://ADFS.example.com/adfs/ls/). So - follow the chain one more link and you should see what's really happening.
To successfully implement SSO so that it works with Salesforce1, you must make sure that the server is configured correctly to support the idosyncrocies of Salesforce1, which is more stringent than other clients (e.g. every major web browser on the market).
This answer documents some common mistakes and outlines how to correctly configure AD FS.
AD FS and AD Cannot Share the same Server Name
During initial testing, we set up a new domain in a new forest, and installed AD and AD FS on the same server. In doing so, AD FS wouldn't correctly handle authentication. The solution to this was to assign a different name to AD FS and point a DNS entry to this server.
In our case, the basic configuration was as follows:
Domain: fear.brian.local
AD Server: ad.fear.brian.local
DNS: ad.fear.brian.local 10.2.0.254
DNS: adfs.fear.brian.local 10.2.0.254
This meant that we had to change the identifier in AD FS Management > AD FS > Edit Federation Service Properties..., and also had to run the following commands:
setspn -s http/adfs ad
setspn -s host/adfs ad
setspn -s http/adfs.fear.brian.local ad
setspn -s host/adfs.fear.brian.local ad
This registers the computer "AD" (for ad.fear.brian.local) as a service principal name for various names that the AD FS server will use.
The identifier in the Federation Service Identifier should be like: https://adfs.fear.brian.local/adfs/services/trust. There is no trailing slash or period on the URL-looking identifier.
You don't need to do this if you have a dedicated AD FS server that is different from AD. DNS may also be hosted on the same server, so if you already have a Windows Server with local DNS resolution, you can reuse that server for the AD FS role.
Salesforce1 Does Not Accept Self-Signed Certificates
When configuring AD FS's SSL feature, you must use a certificate that matches the domain name you specified, and that certificate must be a CA-signed certificate, such as by Verisign. We were unable to determine a way to load a self-signed certificate into our mobile device that would work, and Salesforce1 would refuse to connect to such a server with an error regarding an untrusted certificate with no way to allow or bypass the error. There's also no documentation on this, so we ultimately registered the server we intend to use with our CA, even though it is not Internet-facing.
If you're not going to support Salesforce1 on that server, you can use any valid certificate for SSL. In original testing, we installed the IIS role and generated a certificate for this purpose. Clients such as Outlook and Salesforce Chatter will happily accept a self-signed certificate, and you can install the certificate on your users' browsers if you'd like to avoid a warning message.
Gathering Data From AD FS
Once the AD FS role is installed, and initially configured, setting up single sign on only takes a few minutes of actual work. Here's the most efficient process for configuring a SSO connection.
First, login to the AD FS server, open AD FS Management, and click on AD FS > Edit Federation Service Properties..., and copy the Federation Service identifier somewhere you can reference it later. Next, go into AD FS > Service > Certificates, click on the Token-Signing Certificate, click View Certificate... > Details (tab) > Copy to File... > Next > Next > Choose save location > Next > Finish. Note that this implies the only setting you choose is a file name. All the rest are default values and should be used.
Configuring Salesforce
First, click on Setup > Domain Management > My Domain, choose a domain, and enable it. This process takes a few minutes to register the domain with DNS before you can use it. It doesn't need to be "Deployed to users" until you're ready to go live. Simply being in the phase where it is registered and available for testing will suffice. If your production organization is already configured with a domain, any new or refreshed sandboxes will automatically take this same name, with the sandbox name and server as a suffix. For example, if production is mydomain.my.salesforce.com, your sandbox will be mydomain--sandboxname.cs10.my.salesforce.com (where cs10 is the hosting instance).
Next, click on Setup > Security Controls > Single Sign-On Settings > Edit.
Check "Enable SAML", then choose the following settings:
SAML Version: 2.0
Issuer: https://adfs.fear.brian.local/adfs/services/trust (The Federation Service identifier from AD FS)
Identity Provider Certificate: Browse for the file exported from AD FS.
Identity Provider Login URL: https://adfs.fear.brian.local/adfs/ls/ (replace with your server name)
SAML Identity Type: Assertion contains the Federation ID from the User object
SAML Identity Location: Identity is in the NameIdentifier element of the Subject statement
Entity ID: (My Domain URL, e.g. https://mydomain.my.salesforce.com).
Service Provider Initiated Request Binding: HTTP Redirect
Once you save these settings, click Download Metadata and save this file for use in AD FS.
Configuring AD FS
Back in AD FS Management, click on AD FS > Trust Relationships > Add Relying Party Trust... > Start > "Import data about the relying party from a file" > Browse for the file just downloaded > Next > Provide Display Name (e.g. Salesforce) > Next > Next > Next > Next > Finish.
At this point, you'll see the Claim Rules dialog. Click Add Rule... > Next > Enter a name (e.g. UPN), Attribute Store (Active Directory) > LDAP Attribute "User-Principal-Name" > Outgoing Claim Type "Name ID" > Finish > OK.
Now, click on the newly created trust, click Properties > Advanced > Secure Hash Algorithm "SHA1" > OK.
IMPORTANT: Salesforce doesn't support SHA256. If you don't change the SHA, you'll either fail to login or you'll lose the "relay state", which effecively renders SSO clients unable to login (e.g. Salesforce1, Salesforce for Outlook, Chatter).
Salesforce.com now supports SHA256. However, do you need to make sure that the settings match in both places. In ADFS, check Properties > Advanced > Secure Hash Algorithm, and make sure it matches in salesforce.com under Setup > Security Controls > Single Sign-On Settings > Request Signature Method (if you have multiple configurations enabled, each one has its own setting).
Recovering From Failure
If you can't login by SSO, you'll need to be able to login normally. Just add ?login to your normal login URL to login with your Salesforce credentials. For example: https://mydomain.cs10.my.salesforce.com/?login.
Best Answer
Salesforce1 does not support NTLM authentication. To use SSO with Salesforce1, you must use Forms Authentication. If your AD FS server is Internet-facing (not recommended), you can set external connections to Forms Authentication and internal connections to Windows Authentication, which will allow Salesforce1 to SSO correctly if you're not using a VPN.
Since we are using a VPN as a security requirement, we were forced to disable Windows Authentication on all devices to save the hassle of setting up two servers. Some blogs on the Internet suggest setting up two AD FS servers, setting the SSO login URL to a script that reads the Client header, then redirecting to one of two servers with a 302 redirect depending on Windows Authentication support.
Using this method, you can utilize automatic login when available, and Forms Authentication otherwise, although we chose not to go this route. So, in summary, do not attempt to use NTLM authentication on Salesforce1, because it will never successfully complete the login handshake.