+ Start a Discussion
sfdcfoxsfdcfox 

How do I get an OAuth2 refresh token in Google Chrome Packaged Apps?

Background


I am creating a Google Chrome Packaged App that allows users to access various features from a plurality of salesforce.com organizations. This app will allow users to perform various tasks depending on their permissions in each organization, such as querying data and logging in with a single click. To avoid storing passwords, I'm trying to use OAuth2 to generate refresh tokens that can be used to access those organizations, with a mechanism to revoke those tokens if needed across all organizations in the event of a compromise.

 

Problem


Generating a refresh token only happens in certain flows that follow a certain pattern, and those patterns seem to be incompatible with Google's "chrome.identity.launchWebAuthFlow" mechanism.

 

Attempted Solutions


So far, I have tried the following end-points to try and get Google Chrome to recognize that we've successfully completed the flow and obtain a refresh token. Here are my attempts so far:

 

/services/oauth2/authorize?

grant_type=code&

client_id=XYZ&

prompt=login&

scope=full+refresh_token&

redirect_uri=https://<appid>.chromiumapp.org/callback

 

This link follows the web server flow. We get a code back that we can use to call /services/oauth2/tokens, but ths requires the client_secret, which we are not supposed to embed in the code for security reasons-- the source code is open.

 

/services/oauth2/authorize?

grant_type=token&

client_id=XYZ&

prompt=login&

scope=full+refresh_token&

redirect_uri=https://<appid>.chromiumapp.org/callback

 

This link won't give us a refresh_token, because it does not have a custom protocol and does not match login.salesforce.com/services/oauth2/success.

 

/services/oauth2/authorize?

grant_type=token&

client_id=XYZ&

prompt=login&

scope=full+refresh_token&

redirect_uri=chrome-extension://<appid>/callback

 

This link results in an error in Google Chrome about not being able to load the authorization page, but would give us a refresh token if it worked.

 

/services/oauth2/authorize?

grant_type=token&

client_id=XYZ&

prompt=login&

scope=full+refresh_token&

redirect_uri=https://login.salesforce.com/services/oauth2/success

 

This page doesn't actually show an access token or error, so Google Chrome never detects that the flow is complete. We do not get an access token or refresh token. I have a feeling this is is the proper flow to use, but without a way to detect the token, it simply doesn't work. The authorization page simply sits open forever once we reach this step.

 

Question


How am I supposed to code this app such that it securely accesses salesforce.com without compromising the client_secret while preventing excessive login attempts and/or API calls from a periodic timer that would be required?

 

I realize that this question may be outside the scope of this forum, but this seems to be fairly salesforce.com specific, as other OAuth2 sites work just fine with chrome.identity.launchWebAuthFlow, so I thought I'd ask here and see if anyone knows enough about Chrome and OAuth2 as it pertains to accessing salesforce.com.

Best Answer chosen by Admin (Salesforce Developers) 
sfdcfoxsfdcfox

When using OAuth2 with salesforce.com in a packaged app, you need to use a "webview" element and listen for the appropriate events. Here are the portions of the source that are relevant to the solution:

Page

<!DOCTYPE html>
<html>
...
    <body>
<!-- content required to have page eventually display interactive oauth2 -->
        <webview style="display: none; ... (set a nice width/height)" id="oauth2"></webview>
    </body>
</html>

JavaScript

(function() {
    var outh2frame;
    function showOAuth2Frame() {
        oauth2frame.style.display = 'block';
        oauth2frame.src='https://login.salesforce.com...';
    }
    function onWebViewLoadStart(event) {
        if(event.url.indexOf('/services/oauth2/success#')>-1) {
            // we've got success. Load the tokens and hide the frame.
        }
    }
    function onLoad(event) {
        oauth2frame = document.querySelector('#oauth2');
        oauth2frame.addEventListener('loadstart', onWebViewLoadStart);
    }
    addEventListener('load', onLoad, true);
}());

You also need to add the appropriate permission to your package in order to use a webview.