Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC OAuth authentication with Salesforce returns to login page

I'm trying to get a working example of authenticating an MVC application in .NET against Salesforce using their OAuth authentication workflow. I've been referencing this walkthrough which is fairly simple. It is very similar to getting Google authentication to work. It boils down to setting up the .NET MVC project with the individual account authentication template and spin up a new connected application in Salesforce. Then, add the Owin.Security.Providers library for Salesforce, adjust the Startup.Auth.cs a bit and include the ClientId and ClientSecret from the Salesforce app, and the Authorization and Token endpoints. The callback URL it suggests is http://localhost:[port]/signin-salesforce which is pretty similar to the callback URL used for Google authentication.

While I am redirected to Salesforce and can login, the handshake that returns me back to my MVC application seems to encounter an issue that I cannot nail down. I get redirected back to the Login page and .NET doesn't seem to be aware of my login info, although I definitely have an active session with Salesforce (the Salesforce dashboard will automatically log me in). In the code, things start to go sideways here:

// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
    var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
    if (loginInfo == null)
    {
        return RedirectToAction("Login");
    }
    //more code we never reach
}

loginInfo is always null. So, to figure out what sort of request is being sent to .NET, I turned to Fiddler and encountered a request against localhost:[port]/signin-salesforce with a bunch of parameters that gets a curious response:

HTTP/1.1 302 Found

Location: /Account/ExternalLoginCallback?error=access_denied

Server: Microsoft-IIS/10.0

Set-Cookie: .AspNet.Correlation.Salesforce=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT

X-SourceFiles: =?UTF-8?B?YzpcdXNlcnNcc3RldmUuY2FtaXJlXGRvY3VtZW50c1x2aXN1YWwgc3R1ZGlvIDIwMTVcUHJvamVjdHNcU2FsZXNGb3JjZUludGVncmF0aW9uXFNhbGVzRm9yY2VJbnRlZ3JhdGlvblxzaWduaW4tc2FsZXNmb3JjZQ==?=

X-Powered-By: ASP.NET

Date: Fri, 20 May 2016 21:46:09 GMT

Content-Length: 0

Note the Location header, which tells .NET to redirect to /Account/ExternalLoginCallback with an error parameter of "access_denied". Enabling some tracing in Owin reveals that something is returning a 400 either in .NET or to .NET, but I don't know what.

So, that's where I'm at. A null loginInfo object in my controller and evidence that something is going wrong in some web requests. I've perused several other related questions, but almost none focus on SalesForce and they provide answers that aren't really applicable (for instance, I have no Google+ API to enable). Any ideas on how to rectify this?

like image 764
Ellesedil Avatar asked May 20 '16 22:05

Ellesedil


4 Answers

Looks like the redirect URI is required to match the one on the Identity server. As you are getting redirected to the login page again, looks like the redirect URI might not be matching with the Client secret and Client Id.

In the SalesForce Identity server, there will be a redirect URI that will decide the path where you will get redirected once the authentication is done. Sometimes you also have to provide the URI with Client ID and secret to get completely authorized.

like image 129
Mitra Ghorpade Avatar answered Nov 06 '22 05:11

Mitra Ghorpade


To everyone having issues with the loginInfo always coming back null when it really shouldn't be, This may not fix your issue, however I spend days on a similar problem with facebook and google. I ended up clearing the HttpContext session before ExternalLoginCallback is invoked and it's been working perfect ever since. Not saying this will fix it, but hopefully it helps somebody.

//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
    // Clear any other session first as this seems to cause unexpected null responses.
    if (ControllerContext.HttpContext.Session != null)
    {
        ControllerContext.HttpContext.Session.RemoveAll();
    }

    // Request a redirect to the external login provider
    return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}

When I was testing mine, I checked with Fiddler and I was getting a token back okay, If you are not getting a valid response then this won't help.

like image 34
Joe_DM Avatar answered Nov 06 '22 04:11

Joe_DM


I would check Startup.Auth.cs and verify that your organization has the correct instance and you are not using the one in the walkthrough (if different). My organization, for example, starts with na10.salesforce.com. The walkthrough uses ap1.salesforce.com.

    AuthorizationEndpoint = "https://**ap1**.salesforce.com/services/oauth2/authorize",
    TokenEndpoint = "https://**ap1**.salesforce.com/services/oauth2/token"

I would also ensure that the cookies and cache are cleared. For example, I fired up a brand new VS 2015 MVC project, without any OAuth info and I was logged in via the SFDC provider. No client id, secret, not even a SFDC section!

I did the walkthrough and was able to authenticate with SFDC OAuth. Like you said, it was very straightforward. I wouldn't tear apart your code so much as worry about something simple.

like image 45
smoore4 Avatar answered Nov 06 '22 05:11

smoore4


I further went ahead to download the Owin.Security.Providers.Salesforce code and added it to my project reference to be able to debug the issue. I found out that when the response for the oauth code swap was being performed I received an exception for 'Response status code does not indicate success: 400 (TLS 1.1 or higher required).' I was working off the sandbox and Salesforce has disabled TLS 1.0 starting June 2016. so adding this line of code on my Account Controller Constructor fixed my issue:

 System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
like image 31
Vaibhav Pingle Avatar answered Nov 06 '22 04:11

Vaibhav Pingle