Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for salesforce.com #42

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.DS_Store
samples/*/oauth2
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ Here's a table that will come in handy:
<td>http://www.feedly.com/robots.txt</td>
<td>https://cloud.feedly.com/v3/auth/token</td>
</tr>
<tr>
<td>salesforce</td>
<td>https://login.salesforce.com/services/oauth2/success</td>
<td>https://login.salesforce.com/services/oauth2/token</td>
</tr>
<tr>
<td>salesforce_test</td>
<td>https://test.salesforce.com/services/oauth2/success</td>
<td>https://test.salesforce.com/services/oauth2/token</td>
</tr>
</table>

#### Step 1: Copy library
Expand Down Expand Up @@ -108,7 +118,17 @@ extension access to the OAuth2 endpoint.
api_scope: 'https://www.googleapis.com/auth/tasks'
});

googleAuth.authorize(function() {
googleAuth.authorize(function(error, details) {
if (error) {
// Handle error
return;
}

// The details object is the result of the call to the
// adapters parseAccessToken function - there may be extra
// information returned from the server, e.g. the salesforce
// instanceUrl value when authing with salesforce

// Ready for action, can now make requests with
googleAuth.getAccessToken()
});
Expand Down
60 changes: 60 additions & 0 deletions lib/adapters/salesforce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
OAuth2.adapter('salesforce', {

authorizationCodeURL: function(config) {
return ('https://login.salesforce.com/services/oauth2/authorize?' +
'response_type=code&' +
'client_id={{CLIENT_ID}}&' +
'scope={{API_SCOPE}}&' +
'display=touch&' +
'redirect_uri={{REDIRECT_URI}}')
.replace('{{CLIENT_ID}}', config.clientId)
.replace('{{API_SCOPE}}', config.apiScope)
.replace('{{REDIRECT_URI}}', this.redirectURL(config));
},

redirectURL: function(config) {
return 'https://login.salesforce.com/services/oauth2/success';
},

parseAuthorizationCode: function(url) {
var error = url.match(/[&\?]error=([^&]+)/);
if (error) {
throw 'Error getting authorization code: ' + error[1];
}

url = decodeURIComponent(url);
return url.match(/[&\?]code=([\w\/\-\=\.]+)/)[1];
},

accessTokenURL: function() {
return 'https://login.salesforce.com/services/oauth2/token';
},

accessTokenMethod: function() {
return 'POST';
},

accessTokenParams: function(authorizationCode, config) {
return {
code: authorizationCode,
client_id: config.clientId,
client_secret: config.clientSecret,
redirect_uri: this.redirectURL(config),
grant_type: 'authorization_code'
};
},

parseAccessToken: function(response) {
var values = JSON.parse(response);
return {
accessToken: values.access_token,
refreshToken: values.refresh_token,

// We don't get this info in the response from the salesforce
// server, the value depends on what the admin of the org set
expiresIn: null,

instanceUrl: values.instance_url
};
}
});
60 changes: 60 additions & 0 deletions lib/adapters/salesforce_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
OAuth2.adapter('salesforce_test', {

authorizationCodeURL: function(config) {
return ('https://test.salesforce.com/services/oauth2/authorize?' +
'response_type=code&' +
'client_id={{CLIENT_ID}}&' +
'scope={{API_SCOPE}}&' +
'display=touch&' +
'redirect_uri={{REDIRECT_URI}}')
.replace('{{CLIENT_ID}}', config.clientId)
.replace('{{API_SCOPE}}', config.apiScope)
.replace('{{REDIRECT_URI}}', this.redirectURL(config));
},

redirectURL: function(config) {
return 'https://test.salesforce.com/services/oauth2/success';
},

parseAuthorizationCode: function(url) {
var error = url.match(/[&\?]error=([^&]+)/);
if (error) {
throw 'Error getting authorization code: ' + error[1];
}

url = decodeURIComponent(url);
return url.match(/[&\?]code=([\w\/\-\=\.]+)/)[1];
},

accessTokenURL: function() {
return 'https://test.salesforce.com/services/oauth2/token';
},

accessTokenMethod: function() {
return 'POST';
},

accessTokenParams: function(authorizationCode, config) {
return {
code: authorizationCode,
client_id: config.clientId,
client_secret: config.clientSecret,
redirect_uri: this.redirectURL(config),
grant_type: 'authorization_code'
};
},

parseAccessToken: function(response) {
var values = JSON.parse(response);
return {
accessToken: values.access_token,
refreshToken: values.refresh_token,

// We don't get this info in the response from the salesforce
// server, the value depends on what the admin of the org set
expiresIn: null,

instanceUrl: values.instance_url
};
}
});
2 changes: 1 addition & 1 deletion lib/oauth2.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@
<script src="oauth2.js"></script>
<script src="oauth2_finish.js"></script>
</head>
</html>
</html>
65 changes: 57 additions & 8 deletions lib/oauth2.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,10 @@ OAuth2.prototype.openAuthorizationCodePopup = function(callback) {
* @param {String} authorizationCode Retrieved from the first step in the process
* @param {Function} callback Called back with 3 params:
* access token, refresh token and expiry time
* @param {Function} errorCallback Called with one parameter, the error. The error object contains
* responseText, status and statusText from the xhr object
*/
OAuth2.prototype.getAccessAndRefreshTokens = function(authorizationCode, callback) {
OAuth2.prototype.getAccessAndRefreshTokens = function(authorizationCode, callback, errorCallback) {
var that = this;
// Make an XHR to get the token
var xhr = new XMLHttpRequest();
Expand All @@ -130,6 +132,15 @@ OAuth2.prototype.getAccessAndRefreshTokens = function(authorizationCode, callbac
// Callback with the data (incl. tokens).
callback(that.adapter.parseAccessToken(xhr.responseText));
}
else {
if (!errorCallback) return;

errorCallback({
responseText: xhr.responseText,
status: xhr.status,
statusText: xhr.statusText
});
}
}
});

Expand All @@ -141,6 +152,7 @@ OAuth2.prototype.getAccessAndRefreshTokens = function(authorizationCode, callbac
for (key in items) {
formData.append(key, items[key]);
}

xhr.open(method, that.adapter.accessTokenURL(), true);
xhr.send(formData);
} else if (method == 'GET') {
Expand All @@ -159,8 +171,6 @@ OAuth2.prototype.getAccessAndRefreshTokens = function(authorizationCode, callbac

/**
* Refreshes the access token using the currently stored refresh token
* Note: this only happens for the Google adapter since all other OAuth 2.0
* endpoints don't implement refresh tokens.
*
* @param {String} refreshToken A valid refresh token
* @param {Function} callback On success, called with access token and expiry time and refresh token
Expand Down Expand Up @@ -198,11 +208,11 @@ OAuth2.prototype.finishAuth = function() {
var that = this;

// Loop through existing extension views and excute any stored callbacks.
function callback(error) {
function callback(error, details) {
var views = chrome.extension.getViews();
for (var i = 0, view; view = views[i]; i++) {
if (view['oauth-callback']) {
view['oauth-callback'](error);
view['oauth-callback'](error, details);
// TODO: Decide whether it's worth it to scope the callback or not.
// Currently, every provider will share the same callback address but
// that's not such a big deal assuming that they check to see whether
Expand Down Expand Up @@ -236,15 +246,23 @@ OAuth2.prototype.finishAuth = function() {
}

that.setSource(data);
callback();
callback(null, response);
}, function(error) {
callback(error);
});
};

/**
* @return True iff the current access token has expired
* @return True if the current access token has expired
*/
OAuth2.prototype.isAccessTokenExpired = function() {
var data = this.get();

// If we don't have the expiresIn value, then assume the access
// token has expired
if (!data.expiresIn) {
return true;
}
return (new Date().valueOf() - data.accessTokenDate) > data.expiresIn * 1000;
};

Expand Down Expand Up @@ -460,8 +478,39 @@ OAuth2.prototype.hasAccessToken = function() {
};

/**
* Clears an access token, effectively "logging out" of the service.
* Clears an access token.
*/
OAuth2.prototype.clearAccessToken = function() {
this.clear('accessToken');
};

/**
* @returns A valid refresh token
*/
OAuth2.prototype.getRefreshToken = function() {
return this.get('refreshToken');
};

/**
* Indicate whether or not a valid refresh token exists
*
* @returns {Boolean} True if a refresh token exists; otherwise false;
*/
OAuth2.prototype.hasRefreshToken = function() {
return !!this.get('refreshToken');
};

/**
* Clears a refresh token
*/
OAuth2.prototype.clearRefreshToken = function() {
this.clear('refreshToken');
};

/**
* Clears all stored tokens, effectively logging out of the service
*/
OAuth2.prototype.clearTokens = function() {
this.clearAccessToken();
this.clearRefreshToken();
};
12 changes: 11 additions & 1 deletion samples/mixed/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,22 @@
"matches": ["https://github.com/robots.txt*"],
"js": ["oauth2/oauth2_inject.js"],
"run_at": "document_start"
},
{
"matches": [
"https://test.salesforce.com/services/oauth2/success*",
"https://login.salesforce.com/services/oauth2/success*"
],
"js": ["oauth2/oauth2_inject.js"],
"run_at": "document_start"
}
],
"permissions": [
"https://graph.facebook.com/",
"https://accounts.google.com/o/oauth2/token",
"https://github.com/"
"https://github.com/",
"https://test.salesforce.com/services/oauth2/token",
"https://login.salesforce.com/services/oauth2/token"
],
"web_accessible_resources" : [
"oauth2/oauth2.html"
Expand Down
6 changes: 6 additions & 0 deletions samples/mixed/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,11 @@ <h1>OAuth 2.0 Permissions</h1>
<button id="github">
Grant Github Access
</button>
<button id="salesforce">
Grant Salesforce Access
</button>
<button id="salesforce_test">
Grant Salesforce Test Access
</button>
<button id="clear">Clear Tokens</button>
</body>
22 changes: 18 additions & 4 deletions samples/mixed/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,19 @@

var github = new OAuth2('github', {
client_id: '09450dfdc3ae76768b08',
client_secret: '8ecfc23e0dba1ce1a295fbabc01fa71db4b80261',
client_secret: '8ecfc23e0dba1ce1a295fbabc01fa71db4b80261'
});

var salesforce_test = new OAuth2('salesforce_test', {
client_id: '9MVG982oBBDdwyHjmSlfqa7kDEdYzTMk_07sSeJjETiIWQhnUa_RuV32Te.jt9aP0g8wOB3BOqRBqDJr0m5Cm',
client_secret: '3113638331195393628',
api_scope: ''
});

var salesforce = new OAuth2('salesforce', {
client_id: '9MVG982oBBDdwyHjmSlfqa7kDEdYzTMk_07sSeJjETiIWQhnUa_RuV32Te.jt9aP0g8wOB3BOqRBqDJr0m5Cm',
client_secret: '3113638331195393628',
api_scope: ''
});

function authorize(providerName) {
Expand All @@ -22,7 +34,7 @@

function clearAuthorized() {
console.log('clear');
['google', 'facebook', 'github'].forEach(function(providerName) {
['google', 'facebook', 'github', 'salesforce', 'salesforce_test'].forEach(function(providerName) {
var provider = window[providerName];
provider.clearAccessToken();
});
Expand All @@ -31,7 +43,7 @@

function checkAuthorized() {
console.log('checkAuthorized');
['google', 'facebook', 'github'].forEach(function(providerName) {
['google', 'facebook', 'github', 'salesforce', 'salesforce_test'].forEach(function(providerName) {
var provider = window[providerName];
var button = document.querySelector('#' + providerName);
if (provider.hasAccessToken()) {
Expand All @@ -46,7 +58,9 @@ document.addEventListener('DOMContentLoaded', function () {
document.querySelector('button#google').addEventListener('click', function() { authorize('google'); });
document.querySelector('button#github').addEventListener('click', function() { authorize('github'); });
document.querySelector('button#facebook').addEventListener('click', function() { authorize('facebook'); });
document.querySelector('button#clear').addEventListener('click', function() { clearAuthorized() });
document.querySelector('button#salesforce').addEventListener('click', function() { authorize('salesforce'); });
document.querySelector('button#salesforce_test').addEventListener('click', function() { authorize('salesforce_test'); });
document.querySelector('button#clear').addEventListener('click', function() { clearAuthorized(); });

checkAuthorized();
});
Expand Down