Skip to content

Commit

Permalink
Support OAuth flow for Connect accounts (stripe#555)
Browse files Browse the repository at this point in the history
  • Loading branch information
robz-stripe authored Jan 25, 2019
1 parent 686607f commit 0ff2d99
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 5 deletions.
5 changes: 4 additions & 1 deletion lib/StripeResource.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function StripeResource(stripe, urlData) {
this._stripe = stripe;
this._urlData = urlData || {};

this.basePath = utils.makeURLInterpolator(stripe.getApiField('basePath'));
this.basePath = utils.makeURLInterpolator(this.basePath || stripe.getApiField('basePath'));
this.resourcePath = this.path;
this.path = utils.makeURLInterpolator(this.path);

Expand All @@ -40,6 +40,9 @@ StripeResource.prototype = {

path: '',

// Methods that don't use the API's default '/v1' path can override it with this setting.
basePath: null,

initialize: function() {},

// Function to override the default data processor. This allows full control
Expand Down
55 changes: 55 additions & 0 deletions lib/resources/OAuth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict';

var StripeResource = require('../StripeResource');
var stripeMethod = StripeResource.method;
var utils = require('../utils');

var oAuthHost = 'connect.stripe.com';

module.exports = StripeResource.extend({
basePath: '/',

authorizeUrl: function(params, options) {
params = params || {};
options = options || {};

var path = 'oauth/authorize';

// For Express accounts, the path changes
if (options.express) {
path = 'express/' + path;
}

if (!params.response_type) {
params.response_type = 'code';
}

if (!params.client_id) {
params.client_id = this._stripe.getClientId();
}

if (!params.scope) {
params.scope = 'read_write';
}

return 'https://' + oAuthHost + '/' + path + '?' + utils.stringifyRequestData(params);
},

token: stripeMethod({
method: 'POST',
path: 'oauth/token',
host: oAuthHost,
}),

deauthorize: function(spec) {
if (!spec.client_id) {
spec.client_id = this._stripe.getClientId();
}

return stripeMethod({
method: 'POST',
path: 'oauth/deauthorize',
host: oAuthHost,
}).apply(this, arguments);
},
});
15 changes: 11 additions & 4 deletions lib/stripe.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var APP_INFO_PROPERTIES = ['name', 'version', 'url', 'partner_id'];

var EventEmitter = require('events').EventEmitter;
var exec = require('child_process').exec;
var utils = require('./utils');

var resourceNamespace = require('./ResourceNamespace');

Expand All @@ -51,6 +52,7 @@ var resources = {
Invoices: require('./resources/Invoices'),
IssuerFraudRecords: require('./resources/IssuerFraudRecords'),
LoginLinks: require('./resources/LoginLinks'),
OAuth: require('./resources/OAuth'),
OrderReturns: require('./resources/OrderReturns'),
Orders: require('./resources/Orders'),
PaymentIntents: require('./resources/PaymentIntents'),
Expand Down Expand Up @@ -154,7 +156,6 @@ Stripe.errors = require('./Error');
Stripe.webhooks = require('./Webhooks');

Stripe.prototype = {

setHost: function(host, port, protocol) {
this._setApiField('host', host);
if (port) {
Expand Down Expand Up @@ -234,6 +235,14 @@ Stripe.prototype = {
return this._api[key];
},

setClientId: function(clientId) {
this._clientId = clientId;
},

getClientId: function() {
return this._clientId;
},

getConstant: function(c) {
return Stripe[c];
},
Expand Down Expand Up @@ -292,9 +301,7 @@ Stripe.prototype = {

_prepResources: function() {
for (var name in resources) {
this[
name[0].toLowerCase() + name.substring(1)
] = new resources[name](this);
this[utils.pascalToCamelCase(name)] = new resources[name](this);
}
},

Expand Down
11 changes: 11 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,17 @@ var utils = module.exports = {

return promise;
},

/**
* Allow for special capitalization cases (such as OAuth)
*/
pascalToCamelCase: function(name) {
if (name === 'OAuth') {
return 'oauth';
} else {
return name[0].toLowerCase() + name.substring(1);
}
},
};

function emitWarning(warning) {
Expand Down
123 changes: 123 additions & 0 deletions test/resources/OAuth.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
'use strict';

var stripe = require('../../testUtils').getSpyableStripe();

var expect = require('chai').expect;
var URL = require('url');
var qs = require('qs');

describe('OAuth', function() {
describe('authorize', function() {
describe('when a default client_id is set', function() {
beforeEach(function() {
stripe.setClientId('default_client_id');
});

it('Uses the correct host', function() {
var url = stripe.oauth.authorizeUrl();

var host = URL.parse(url).hostname;

expect(host).to.equal('connect.stripe.com');
});

it('Uses the correct path', function() {
var url = stripe.oauth.authorizeUrl({state: 'some_state'});

var pathname = URL.parse(url).pathname;

expect(pathname).to.equal('/oauth/authorize');
});

it('Uses the correct query', function() {
var url = stripe.oauth.authorizeUrl({state: 'some_state'});

var query = qs.parse(URL.parse(url).query)

expect(query.client_id).to.equal('default_client_id');
expect(query.response_type).to.equal('code');
expect(query.scope).to.equal('read_write');
expect(query.state).to.equal('some_state');
});

it('Uses a provided client_id instead of the default', function() {
var url = stripe.oauth.authorizeUrl({client_id: '123abc'});

var query = qs.parse(URL.parse(url).query)

expect(query.client_id).to.equal('123abc');
});

describe('for Express account', function() {
it('Uses the correct path', function() {
var url = stripe.oauth.authorizeUrl({}, {express: true});

var pathname = URL.parse(url).pathname;

expect(pathname).to.equal('/express/oauth/authorize');
});
});
});
});

describe('token', function() {
it('Sends the correct request', function() {
stripe.oauth.token({
code: '123abc',
grant_type: 'authorization_code'
});

expect(stripe.LAST_REQUEST).to.deep.equal({
method: 'POST',
host: 'connect.stripe.com',
url: '/oauth/token',
headers: {},
data: {
code: '123abc',
grant_type: 'authorization_code'
},
});
});
});

describe('deauthorize', function() {
beforeEach(function() {
stripe.setClientId('default_client_id');
});

it('Sends the correct request without explicit client_id', function() {
stripe.oauth.deauthorize({
stripe_user_id: 'some_user_id',
});

expect(stripe.LAST_REQUEST).to.deep.equal({
method: 'POST',
host: 'connect.stripe.com',
url: '/oauth/deauthorize',
headers: {},
data: {
client_id: stripe.getClientId(),
stripe_user_id: 'some_user_id'
},
});
});

it('Sends the correct request with explicit client_id', function() {
stripe.oauth.deauthorize({
stripe_user_id: 'some_user_id',
client_id: '123abc',
});

expect(stripe.LAST_REQUEST).to.deep.equal({
method: 'POST',
host: 'connect.stripe.com',
url: '/oauth/deauthorize',
headers: {},
data: {
client_id: '123abc',
stripe_user_id: 'some_user_id'
},
});
});
});
});

0 comments on commit 0ff2d99

Please sign in to comment.