14

I'm logging in users via REST in my .NET application. For that in the WebBrowser control constructor I do the following:

string server = "https://login.salesforce.com/";
var authURI = new StringBuilder();
authURI.Append(server + "services/oauth2/authorize?");
authURI.Append("response_type=code");
authURI.Append("&client_id=" + clientID);
authURI.Append("&redirect_uri=" + redirectURL);
webBrowser1.Navigate(authURI.ToString());

This works fine, the user is being presented the standard sfdc login screen, he/she logs in, I do all the flow to get the security token and the user is able to work with SFDC.

Interesting stuff happens after the user logs out, and tries to log in again (e.g. under a different name). He/she clicks the login button, the code above runs again, but instead of showing the SFDC login UI again, salesforce just logs the user in automatically and redirects to the RedirectURI, kicking off the login flow. Thus the user has no way to log in under different credentials... I was sure it was because of some cookie SFDC leaves behind, but after deleting all the cookies the user still gets logged in automatically...

  • 2
    Is your app clearing the token or accidentally reusing it? – Mike Chale Jan 19 '14 at 1:34
  • 1
    This is technically a problem with OAuth2 and the developer's use of a WebBrowser control, and not a problem with salseforce.com itself... I'm still going to upvote it, though, because SFSE would definitely benefit from having an answer for this. I strongly recommend the OP also posts this on stack overflow for more coverage/visibility. I'm decent with .Net, but no expert, and this is going to require some serious focus. – sfdcfox Jan 19 '14 at 11:36
  • I wonder if calling Dispose() on the web browser control and instantiating a new one would help. – Mike Chale Jan 19 '14 at 13:43
  • I'm instantiating a new WebBrowser instance every time, don't call Dispose() on it, but I assumed garbage collector would take of it... – taralex Jan 20 '14 at 2:55
6

When you use OAuth2 in a web browsing context, two things happen: (1) the user has to log in to salesforce.com and receive a "sid" (session ID), then (2) the user has to authorize the app if it is not already authorized. What this means is that the user is logged in twice, once with a session ID, and once with a access token.

As long as any browser process from that browser is active, the "sid" cookie will remain valid, up until the point where the sid expires from a session timeout (specified under security controls, session settings). Presumably, your logout function calls /services/oauth2/revoke, which you presume is logging out of both sessions, but is really only revoking the oauth2 access token.

You can prove this by going to your web browser, and visiting your salesforce instance's /home/home.jsp page after logging in from your app; you'll magically be logged in with no prompt. Conversely, log in to salesforce.com, then call your oauth2 login logic...

Here are a few workarounds. Some are kind of destructive (as in, they'll be logged out of salesforce.com if they're working in a browser window...), but you can try them out:

  1. Call /secur/logout.jsp before presenting the oauth2 dialog. This will log them out of their browser, which may prevent them from automatically logging back in (or, it might just select a different user they're logged in to recently...).

  2. Call the multi-site logout page (the link presented on the OAuth2 grant authorization page that reads "Not you?") before attempting to display the OAuth2 dialog. This will log them out of all instances of salesforce, and is probably not user friendly. I don't have this link handy, but should I find it shortly, I'll update this answer.

  3. Figure out how to invoke Private Browsing on the web browser, or an entirely separate browser process, which should create an isolated cookie context. I did a brief search on Google, but didn't come up with a relevant link for this solution. I presume it is most likely possible, I just don't know how. You could probably load MSHTML.dll into your process space and manipulate it directly as an isolated browser process.

  4. Detect the browser's process and manually delete all salesforce.com cookies, then restore them afterwards (to prevent logging them out of their browser as well). I'm not sure this is easy, or even entirely possible, but you might find something on this path as well.

  • but I tried deleting all cookies before logging in again - to no avail. the second time I log in, Salesforce.com logs me in automatically. – taralex Jan 20 '14 at 3:01
  • @taralex Those cookies are pesky. I'm not sure how how else to help you. All I know is that if you have a SID, you have a valid session, and you will be automatically logged in. I personally find this to be a problem, because I lose control of whom (which one of my accounts) I am logging in as. I wrestle with this every day on the community forums. There should be a smarter UI, like maybe asking me which account I meant, like how Google does it. – sfdcfox Jan 20 '14 at 4:44
4

Posted it on StackExchange and got a great answer that helped solve this: https://stackoverflow.com/questions/21248639/using-net-webbrowser-control-why-salesforce-com-logs-the-user-in-automatically

2

I ran into a similar issue while building a Sinatra-based (ruby) web app for the Salesforce chatter service. Spent ages trying to get the user logout to actually logout and return to the custom app's login page.

Combining the info from the posts here and in the linked post above, I setup this html button:

<a id="logout" href="" >Logout</a>

and this javascript (with jquery) :

$('#logout').click(function() {

  $('#layout-page').append('<iframe style="height:0px;overflow:hidden;" src="https://na15.salesforce.com/secur/logout.jsp?display=touch"/>');

  window.location="/unauthenticate";  //expire app token and redirect to index page which will bring up login screen in my app
});

The relevant Sinatra code:

  get '/*' do

    if (session[:auth_hash] == nil) then
      redirect '/authenticate'
      #redirect '/test'
    else
        redirect '/feed'
    end

    #erb :index, :layout => :layout 
  end   

  get '/unauthenticate' do
    puts session[:auth_hash]
    token = session[:auth_hash].credentials[:token]
    session.clear
    session[:auth_hash] = nil


    curl_cmd = "https://login.salesforce.com/services/oauth2/revoke?token=#{token}"
    puts "curl cmd: #{curl_cmd}"
    `curl #{curl_cmd}`

    redirect '/'
  end

This worked well.

2

This may be helpful for someone who had a similar issue. Salesforce started to support a 'prompt' param as part of OAuth workflow since Winter's 15 (V32). The current version is V36 (2016) and you can find more info here.

If you include prompt=login as part of OAuth url, it forces the server to ask user to login again even if user has already had a session. This solves the issue above where the user gets logged in automatically after revoking an access token. This also allows user to switch to another account for login as well.

Just simply adding it to the end of url like this:

https://login.salesforce.com/services/oauth2/authorize?response_type=code&client_id=xxx&redirect_uri=https%3A%2F%2Fwww.mysite.com%2F&state=mystate&prompt=login

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.