Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

grant

Package Overview
Dependencies
Maintainers
1
Versions
100
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

grant - npm Package Compare versions

Comparing version 3.4.0 to 3.5.0

28

CHANGELOG.md
## Change Log
### v3.5.0 (2015/08/30)
- `Changed` better OAuth2 [random state string](https://github.com/simov/grant/commit/e1cf1e468846e5b2e75f65d8bdf4794a88619c37)
- `Added` ability to override the [redirect_uri](https://github.com/simov/grant#sandbox-redirect-uri)
- `Added` ability to configure Grant without having a *server* key
- `Added` generic error handler for missing or misconfigured provider
- `Added` introduced `custom_params` option for safer way to define [custom authorization parameters](https://github.com/simov/grant#custom-parameters)
- `Added` improved documentation about all configuration [quirks](https://github.com/simov/grant#quirks)
- `Added` official support for 5 new providers
### v3.4.0 (2015/07/20)
- `Changed` better configuration initialization
- `Changed` bumped module dependency versions
- `Changed` migrated *rdio* to [OAuth2](https://github.com/simov/grant/blob/3.4.0/config/oauth.json#L420-L424)
- `Changed` updated the *trakt* [urls](https://github.com/simov/grant/blob/3.4.0/config/oauth.json#L542-L546)
- `Added` [custom_parameters](https://github.com/simov/grant/blob/3.4.0/config/oauth.json#L655) for *yandex*
- `Added` docs about the [programmatic access](https://github.com/simov/grant#programmatic-access)
- `Added` docs about how to utilize [sandbox urls](https://github.com/simov/grant#custom-parameters)
- `Added` official support for 3 more providers
- `Changed` bumped module dependency versions
- `Changed` migrated *rdio* to [OAuth2](https://github.com/simov/grant/blob/master/config/oauth.json#L420-L424)
- `Changed` updated the *trakt* [urls](https://github.com/simov/grant/blob/master/config/oauth.json#L543-L544)
- `Added` [custom_parameters](https://github.com/simov/grant/blob/master/config/oauth.json#L655) for *yandex*
- `Added` official support for 3 new providers
### v3.3.3 (2015/06/24)
- `Added` official support for 9 more providers
- `Added` official support for 9 new providers
### v3.3.2 (2015/06/05)
- `Changed` a few minor changes to the project's meta files
- `Added` official support for 2 more providers
- `Added` official support for 2 new providers
### v3.3.1 (2015/05/21)
- `Added` official support for 10 more providers
- `Added` official support for 10 new providers

@@ -64,3 +72,3 @@ ### v3.3.0 (2015/05/17)

- `Changed` the [response data](https://github.com/simov/grant#response-data) format, now containing a `raw` key in it
- `Changed` [custom authorization parameters](https://github.com/simov/grant#quirks) regarding token expiration are no longer part of the scope array
- `Changed` [custom authorization parameters](https://github.com/simov/grant#custom-parameters) regarding token expiration are no longer part of the scope array

@@ -67,0 +75,0 @@

@@ -308,3 +308,6 @@ {

"scope_delimiter": " ",
"custom_parameters": ["access_type"]
"custom_parameters": [
"access_type", "approval_prompt", "login_hint", "include_granted_scopes",
"prompt"
]
},

@@ -421,2 +424,14 @@ "harvest": {

},
"pushbullet": {
"authorize_url": "https://www.pushbullet.com/authorize",
"access_url": "https://api.pushbullet.com/oauth2/token",
"oauth": 2
},
"ravelry": {
"request_url": "https://www.ravelry.com/oauth/request_token",
"authorize_url": "https://www.ravelry.com/oauth/authorize",
"access_url": "https://www.ravelry.com/oauth/access_token",
"oauth": 1,
"scope_delimiter": " "
},
"rdio": {

@@ -544,2 +559,7 @@ "authorize_url": "https://www.rdio.com/oauth2/authorize",

},
"todoist": {
"authorize_url": "https://todoist.com/oauth/authorize",
"access_url": "https://todoist.com/oauth/access_token",
"oauth": 2
},
"trakt": {

@@ -560,3 +580,3 @@ "authorize_url": "https://api-v2launch.trakt.tv/oauth/authorize",

"oauth": 1,
"custom_parameters": ["scope", "expiration"]
"custom_parameters": ["name", "expiration"]
},

@@ -626,2 +646,7 @@ "tripit": {

},
"weibo": {
"authorize_url": "https://api.weibo.com/oauth2/authorize",
"access_url": "https://api.weibo.com/oauth2/access_token",
"oauth": 2
},
"withings": {

@@ -639,2 +664,7 @@ "request_url": "https://oauth.withings.com/account/request_token",

},
"wrike": {
"authorize_url": "https://www.wrike.com/oauth2/authorize",
"access_url": "https://www.wrike.com/oauth2/token",
"oauth": 2
},
"xing": {

@@ -641,0 +671,0 @@ "request_url": "https://api.xing.com/v1/request_token",

@@ -18,6 +18,12 @@ [

"secret",
"consumer_key",
"consumer_secret",
"client_id",
"client_secret",
"scope",
"redirect_uri",
"name",
"overrides"
"overrides",
"custom_params"
]
'use strict'
var crypto = require('crypto')
var dcopy = require('deep-copy')

@@ -11,12 +12,47 @@

// oauth credentials transform
exports.credentials = function (provider, options) {
var key, secret
// generate provider options
exports.initProvider = function (provider, options, server, name) {
// merge provider options with user options
// cleanup empty values custom_params
if (options.custom_params) {
var params = options.custom_params
for (var key in params) {
if (!params[key]) delete params[key]
}
if (!Object.keys(params).length) {
delete options.custom_params
}
}
// set reserved keys
this.reserved.forEach(function (key) {
var value = options[key] || server[key] || provider[key]
if (value) {
provider[key] = value
}
})
// transformations
// provider shortcuts
if (name) {
provider[name] = true
provider.name = name
}
// oauth credentials
var key = null, secret = null
if (provider.oauth == 1) {
key = options.consumer_key || provider.key
secret = options.consumer_secret || provider.secret
key = provider.consumer_key || provider.key
secret = provider.consumer_secret || provider.secret
}
else if (provider.oauth == 2) {
key = options.client_id || provider.key
secret = options.client_secret || provider.secret
key = provider.client_id || provider.key
secret = provider.client_secret || provider.secret
}

@@ -29,86 +65,41 @@ if (key) {

}
}
// oauth scope transform
exports.scope = function (provider, options) {
var scope = options.scope || provider.scope
if (!scope) return
provider.scope = (scope instanceof Array)
? scope.join(provider.scope_delimiter||',')
: scope
if (provider.copy && typeof scope === 'object') {
provider.scope = JSON.stringify(scope)
// oauth scope
if (provider.scope) {
if (provider.scope instanceof Array) {
provider.scope = provider.scope.join(provider.scope_delimiter||',')
}
else if (typeof provider.scope == 'object') {
provider.scope = JSON.stringify(provider.scope)
}
}
}
// oauth state transform
exports.state = function (provider) {
var state
if (typeof provider.state == 'string' || typeof provider.state == 'number') {
state = provider.state.toString()
}
else if (typeof provider.state == 'boolean' && provider.state) {
state = (Math.floor(Math.random() * 999999) + 1).toString()
}
return state
}
// override provider
exports.override = function (provider, options) {
var override = dcopy(provider)
for (var key in options) {
if (!options[key]) continue
override[key] = options[key]
}
this.transform(override, options)
return override
}
// apply multiple transformations
exports.transform = function (provider, options) {
this.credentials(provider, options)
this.scope(provider, options)
}
// generate provider options
exports.initProvider = function (key, config) {
// oauth provider settings
var provider = dcopy(this.oauth[key]||{})
// oauth application options
var options = config[key]||{}
// provider shortcuts
provider[key] = true
provider.name = key
// set reserved keys
this.reserved.forEach(function (key) {
var value = options[key] || config.server[key] || provider[key]
if (value) {
provider[key] = value
}
})
// custom parameters
// custom_parameters
if (provider.custom_parameters) {
var params = provider.custom_params || {}
for (var key in options) {
if (typeof options[key] === 'string' &&
this.reserved.indexOf(key) == -1 &&
if (this.reserved.indexOf(key) == -1 &&
provider.custom_parameters.indexOf(key) != -1) {
provider[key] = options[key]
params[key] = options[key]
}
}
if (Object.keys(params).length) {
provider.custom_params = params
}
}
// static overrides
var overrides = {}
for (var key in options) {
if (provider.custom_parameters &&
provider.custom_parameters.indexOf(key) != -1) continue
if (this.reserved.indexOf(key) == -1 &&
key != 'scope' &&
typeof options[key] === 'object') {
overrides[key] = this.override(provider, options[key])
overrides[key] = this.initProvider(dcopy(provider), options[key], {})
}

@@ -120,3 +111,3 @@ }

this.transform(provider, options)
return provider

@@ -127,22 +118,50 @@ }

exports.init = function (config) {
config = config||{}
var result = {}
config = config || {}
var server = config.server || {}
// generate provider options
var result = {}
for (var key in config) {
var provider = this.initProvider(key, config)
result[provider.name] = provider
if (key == 'server') continue
var provider = dcopy(this.oauth[key]||{})
, options = config[key]||{}
var generated = this.initProvider(provider, options, server, key)
result[generated.name] = generated
}
result.server = config.server||{}
result.server = server
return result
}
// oauth state transform
exports.state = function (provider) {
var state
if (typeof provider.state == 'string' || typeof provider.state == 'number') {
state = provider.state.toString()
}
else if (typeof provider.state == 'boolean' && provider.state) {
state = crypto.randomBytes(10).toString('hex')
}
return state
}
// get provider on connect
exports.provider = function (config, session) {
var provider = config[session.provider]
var name = session.provider
, provider = config[name]
if (!provider) {
provider = this.initProvider(session.provider, config)
config[provider.name] = provider
if (this.oauth[name]) {
var provider = dcopy(this.oauth[name])
, options = {}
, server = config.server || {}
provider = this.initProvider(provider, options, server, name)
config[provider.name] = provider
} else {
provider = {}
}
}
if (session.override && provider.overrides) {

@@ -152,5 +171,10 @@ var override = provider.overrides[session.override]

}
if (session.dynamic) {
provider = this.override(provider, session.dynamic)
var provider = dcopy(provider)
, options = session.dynamic
, server = config.server || {}
provider = this.initProvider(provider, options, server)
}
if (provider.state) {

@@ -160,3 +184,4 @@ provider = dcopy(provider)

}
return provider
}

@@ -32,6 +32,10 @@ 'use strict'

req.session.grant = {
provider:req.params.provider,
override:req.params.override,
dynamic:req.query
provider:req.params.provider
}
if (req.params.override) {
req.session.grant.override = req.params.override
}
if (Object.keys(req.query||{}).length) {
req.session.grant.dynamic = req.query
}

@@ -43,6 +47,10 @@ connect(req, res)

req.session.grant = {
provider:req.params.provider,
override:req.params.override,
dynamic:req.body
provider:req.params.provider
}
if (req.params.override) {
req.session.grant.override = req.params.override
}
if (Object.keys(req.body||{}).length) {
req.session.grant.dynamic = req.body
}

@@ -80,6 +88,15 @@ connect(req, res)

}
else {
var err = {error:'Grant: missing or misconfigured provider'}
if (provider.callback) {
res.redirect(provider.callback + '?' + qs.stringify(err))
} else {
res.end(JSON.stringify(err))
}
}
}
app.get('/connect/:provider/callback', function (req, res) {
var grant = req.session.grant
var grant = req.session.grant || {}
var provider = config.provider(app.config, grant)

@@ -119,2 +136,11 @@ var flow = flows[provider.oauth]

}
else {
var err = {error:'Grant: missing session or misconfigured provider'}
if (provider.callback) {
res.redirect(provider.callback + '?' + qs.stringify(err))
} else {
res.end(JSON.stringify(err))
}
}
})

@@ -121,0 +147,0 @@

@@ -30,8 +30,17 @@ 'use strict'

if (!req.session) throw new Error('Grant: register session plugin first')
req.session.set('grant', {
provider:req.params.provider,
override:req.params.override||undefined,
dynamic:(req.method == 'post') ? (req.payload||{}) : (req.query||{})
})
var session = {
provider:req.params.provider
}
if (req.params.override) {
session.override = req.params.override
}
if (req.method == 'get' && Object.keys(req.query||{}).length) {
session.dynamic = req.query
}
if (req.method == 'post' && Object.keys(req.payload||{}).length) {
session.dynamic = req.payload
}
req.session.set('grant', session)
connect(req, res)

@@ -69,2 +78,11 @@ }

}
else {
var err = {error:'Grant: missing or misconfigured provider'}
if (provider.callback) {
res.redirect(provider.callback + '?' + qs.stringify(err))
} else {
res(JSON.stringify(err))
}
}
}

@@ -76,3 +94,3 @@

handler: function (req, res) {
var grant = req.session.get('grant')
var grant = req.session.get('grant') || {}
var provider = config.provider(self.config, grant)

@@ -113,2 +131,11 @@ var flow = flows[provider.oauth]

}
else {
var err = {error:'Grant: missing session or misconfigured provider'}
if (provider.callback) {
res.redirect(provider.callback + '?' + qs.stringify(err))
} else {
res(JSON.stringify(err))
}
}
}

@@ -115,0 +142,0 @@ })

@@ -39,6 +39,10 @@ 'use strict'

this.session.grant = {
provider:provider,
override:override,
dynamic:this.request.query
provider:provider
}
if (override) {
this.session.grant.override = override
}
if (Object.keys(this.request.query||{}).length) {
this.session.grant.dynamic = this.request.query
}

@@ -50,6 +54,10 @@ yield connect

this.session.grant = {
provider:provider,
override:override,
dynamic:this.request.body
provider:provider
}
if (override) {
this.session.grant.override = override
}
if (Object.keys(this.request.body||{}).length) {
this.session.grant.dynamic = this.request.body
}

@@ -93,6 +101,15 @@ yield connect

}
else {
var err = {error:'Grant: missing or misconfigured provider'}
if (provider.callback) {
this.response.redirect(provider.callback + '?' + qs.stringify(err))
} else {
this.body = JSON.stringify(err)
}
}
}
function* callback () {
var grant = this.session.grant
var grant = this.session.grant || {}
var provider = config.provider(app.config, grant)

@@ -138,2 +155,11 @@ var flow = flows[provider.oauth]

}
else {
var err = {error:'Grant: missing session or misconfigured provider'}
if (provider.callback) {
this.response.redirect(provider.callback + '?' + qs.stringify(err))
} else {
this.body = JSON.stringify(err)
}
}
}

@@ -140,0 +166,0 @@

@@ -24,3 +24,3 @@ 'use strict'

if (provider.subdomain) {
url = url.replace('[subdomain]',provider.subdomain)
url = url.replace('[subdomain]', provider.subdomain)
}

@@ -37,3 +37,4 @@ request.post(url, options, function (err, res, body) {

if (!step1.oauth_token) {
var error = (Object.keys(step1).length) ? step1 : {error:'Grant: request_url'}
var error = (Object.keys(step1).length)
? step1 : {error:'Grant: OAuth1 missing oauth_token parameter'}
return provider.callback + '?' + utils.toQuerystring({}, error, true)

@@ -45,9 +46,13 @@ }

}
if (provider.custom_parameters) {
provider.custom_parameters.forEach(function (key) {
params[key] = (provider.flickr && key == 'perms')
? provider.scope
: provider[key]
})
if (provider.custom_params) {
for (var key in provider.custom_params) {
params[key] = provider.custom_params[key]
}
}
if (provider.flickr) {
params.perms = provider.scope
}
if (provider.ravelry || provider.trello) {
params.scope = provider.scope
}
if (provider.tripit) {

@@ -57,3 +62,3 @@ params.oauth_callback = utils.redirect_uri(provider)

if (provider.subdomain) {
url = url.replace('[subdomain]',provider.subdomain)
url = url.replace('[subdomain]', provider.subdomain)
}

@@ -65,3 +70,4 @@ return url + '?' + qs.stringify(params)

if (!step2.oauth_token) {
var error = (Object.keys(step2).length) ? step2 : {error:'Grant: authorize_url'}
var error = (Object.keys(step2).length)
? step2 : {error:'Grant: OAuth1 missing oauth_token parameter'}
return done(utils.toQuerystring({}, error, true))

@@ -86,3 +92,3 @@ }

if (provider.subdomain) {
url = url.replace('[subdomain]',provider.subdomain)
url = url.replace('[subdomain]', provider.subdomain)
}

@@ -89,0 +95,0 @@ request.post(url, options, function (err, res, body) {

@@ -17,15 +17,12 @@ 'use strict'

}
if (provider.custom_params) {
for (var key in provider.custom_params) {
params[key] = provider.custom_params[key]
}
}
if (provider.basecamp) {
params.type = 'web_server'
}
if (provider.surveymonkey) {
params.api_key = provider.api_key
}
if (provider.custom_parameters) {
provider.custom_parameters.forEach(function (key) {
params[key] = provider[key]
})
}
if (provider.subdomain) {
url = url.replace('[subdomain]',provider.subdomain)
url = url.replace('[subdomain]', provider.subdomain)
}

@@ -37,3 +34,4 @@ return url + '?' + qs.stringify(params)

if (!step1.code) {
var error = (Object.keys(step1).length) ? step1 : {error:'Grant: authorize_url'}
var error = (Object.keys(step1).length)
? step1 : {error:'Grant: OAuth2 missing code parameter'}
return done(utils.toQuerystring({}, error, true))

@@ -58,5 +56,2 @@ }

}
if (provider.surveymonkey) {
options.qs = {api_key:provider.api_key}
}
if (provider.reddit) {

@@ -67,4 +62,7 @@ delete options.form.client_id

}
if (provider.surveymonkey) {
options.qs = {api_key:provider.custom_params.api_key}
}
if (provider.subdomain) {
url = url.replace('[subdomain]',provider.subdomain)
url = url.replace('[subdomain]', provider.subdomain)
}

@@ -71,0 +69,0 @@ request.post(url, options, function (err, res, body) {

@@ -7,2 +7,5 @@ 'use strict'

exports.redirect_uri = function (provider) {
if (provider.redirect_uri) {
return provider.redirect_uri
}
var url = [

@@ -9,0 +12,0 @@ provider.protocol,

{
"name": "grant",
"version": "3.4.0",
"version": "3.5.0",
"description": "OAuth Middleware for Express, Koa and Hapi",

@@ -33,7 +33,7 @@

"devDependencies": {
"mocha" : "2.2.4",
"should" : "5.2.0",
"istanbul" : "0.3.13",
"coveralls" : "2.11.2",
"eslint" : "0.19.0",
"mocha" : "2.x.x",
"should" : "7.x.x",
"istanbul" : "0.x.x",
"coveralls" : "2.x.x",
"eslint" : "1.x.x",

@@ -44,13 +44,12 @@ "express" : "4.x.x",

"koa" : "0.x.x",
"koa-route" : "2.4.0",
"thunkify" : "2.1.2",
"koa" : "1.x.x",
"koa-route" : "2.x.x",
"thunkify" : "2.x.x",
"koa-session" : "3.x.x",
"koa-bodyparser" : "1.x.x",
"koa-mount" : "1.3.0",
"koa-router" : "3.7.0",
"koa-qs" : "2.0.0",
"koa-bodyparser" : "2.x.x",
"koa-mount" : "1.x.x",
"koa-qs" : "2.x.x",
"hapi" : "8.x.x",
"hapi" : "9.x.x",
"yar" : "3.x.x"

@@ -75,4 +74,4 @@ },

"lint-lib" : "eslint lib/ && echo Lint lib passed",
"lint-test" : "eslint --config test/.eslintrc test/ && echo Lint test passed"
"lint-test" : "eslint test/ && echo Lint test passed"
}
}

@@ -7,5 +7,5 @@

## 100+ Supported Providers / [OAuth Playground][playground]
## 100+ Supported Providers / [OAuth Playground][grant-oauth]
[`23andme`](https://api.23andme.com) | [`500px`](http://developers.500px.com) | [`acton`](https://developer.act-on.com) | [`amazon`](http://login.amazon.com/documentation) | [`angellist`](https://angel.co/api) | [`appnet`](https://developers.app.net) | [`asana`](https://asana.com/developers) | [`assembla`](http://api-doc.assembla.com) | [`basecamp`](https://github.com/basecamp/bcx-api) | [`beatport`](https://oauth-api.beatport.com) | [`beatsmusic`](https://developer.beatsmusic.com) | [`bitbucket`](https://confluence.atlassian.com/display/BITBUCKET) | [`bitly`](http://dev.bitly.com) | [`box`](https://developers.box.com) | [`buffer`](https://dev.buffer.com) | [`campaignmonitor`](https://www.campaignmonitor.com/api) | [`cheddar`](https://cheddarapp.com/developer) | [`coinbase`](https://developers.coinbase.com) | [`constantcontact`](https://developer.constantcontact.com) | [`copy`](https://developers.copy.com) | [`coursera`](https://tech.coursera.org) | [`dailymile`](http://www.dailymile.com/api/documentation) | [`dailymotion`](https://developer.dailymotion.com) | [`deezer`](http://developers.deezer.com) | [`delivery`](https://developers.delivery.com) | [`deviantart`](https://www.deviantart.com/developers/) | [`digitalocean`](https://developers.digitalocean.com) | [`discogs`](http://www.discogs.com/developers) | [`disqus`](https://disqus.com/api/docs) | [`dribbble`](http://developer.dribbble.com) | [`dropbox`](https://www.dropbox.com/developers) | [`echosign`](https://secure.echosign.com/public/docs/restapi/v3) | [`edmodo`](https://developers.edmodo.com) | [`elance`](https://www.elance.com/q/api2) | [`etsy`](https://www.etsy.com/developers) | [`eventbrite`](http://developer.eventbrite.com) | [`evernote`](https://dev.evernote.com) | [`everyplay`](https://developers.everyplay.com) | [`eyeem`](https://www.eyeem.com/developers) | [`facebook`](https://developers.facebook.com) | [`familysearch`](https://familysearch.org/developers) | [`feedly`](https://developer.feedly.com) | [`fitbit`](http://dev.fitbit.com) | [`flattr`](http://developers.flattr.net) | [`flickr`](https://www.flickr.com/services) | [`flowdock`](https://www.flowdock.com/api) | [`foursquare`](https://developer.foursquare.com) | [`freshbooks`](https://www.freshbooks.com/developers) | [`geeklist`](http://hackers.geekli.st) | [`getpocket`](http://getpocket.com/developer) | [`github`](https://developer.github.com) | [`gitlab`](http://doc.gitlab.com/ce/api) | [`gitter`](https://developer.gitter.im) | [`goodreads`](https://www.goodreads.com/api) | [`google`](https://developers.google.com) | [`harvest`](https://github.com/harvesthq/api) | [`heroku`](https://devcenter.heroku.com/categories/platform-api) | [`imgur`](https://api.imgur.com) | [`instagram`](https://instagram.com/developer) | [`jawbone`](https://jawbone.com/up/developer) | [`kakao`](https://developers.kakao.com) | [`linkedin`](https://developer.linkedin.com) | [`live`](https://msdn.microsoft.com/en-us/library/dn783283.aspx) | [`mailchimp`](https://apidocs.mailchimp.com) | [`mapmyfitness`](https://developer.underarmour.com) | [`meetup`](http://www.meetup.com/meetup_api) | [`mixcloud`](https://www.mixcloud.com/developers) | [`moves`](https://dev.moves-app.com) | [`myob`](http://developer.myob.com) | [`odesk`](https://developers.odesk.com) | [`openstreetmap`](http://wiki.openstreetmap.org/wiki/API_v0.6) | [`paypal`](https://developer.paypal.com) | [`plurk`](http://www.plurk.com/API) | [`podio`](https://developers.podio.com) | [`rdio`](http://www.rdio.com/developers) | [`redbooth`](https://redbooth.com/api) | [`reddit`](http://www.reddit.com/dev/api) | [`runkeeper`](http://developer.runkeeper.com) | [`salesforce`](https://developer.salesforce.com) | [`shoeboxed`](https://github.com/Shoeboxed/api) | [`shopify`](https://docs.shopify.com/api) | [`skyrock`](http://www.skyrock.com/developer) | [`slack`](https://api.slack.com) | [`slice`](https://developer.slice.com) | [`socrata`](http://dev.socrata.com) | [`soundcloud`](https://developers.soundcloud.com) | [`spotify`](https://developer.spotify.com) | [`square`](https://connect.squareup.com) | [`stackexchange`](https://api.stackexchange.com) | [`stocktwits`](http://stocktwits.com/developers) | [`stormz`](http://developer.stormz.me) | [`strava`](http://strava.github.io/api) | [`stripe`](https://stripe.com/docs) | [`surveygizmo`](http://apihelp.surveygizmo.com) | [`surveymonkey`](https://developer.surveymonkey.com) | [`thingiverse`](http://www.thingiverse.com/developers) | [`trakt`](http://docs.trakt.apiary.io) | [`traxo`](https://developer.traxo.com) | [`trello`](https://trello.com/docs) | [`tripit`](https://www.tripit.com/developer) | [`tumblr`](https://www.tumblr.com/docs/en/api/v2) | [`twitch`](http://dev.twitch.tv) | [`twitter`](https://dev.twitter.com) | [`uber`](https://developer.uber.com) | [`underarmour`](https://developer.underarmour.com) | [`upwork`](https://developers.upwork.com) | [`uservoice`](https://developer.uservoice.com) | [`vend`](https://developers.vendhq.com) | [`vimeo`](https://developer.vimeo.com) | [`vk`](http://vk.com/dev) | [`withings`](http://oauth.withings.com/api) | [`wordpress`](https://developer.wordpress.com) | [`xing`](https://dev.xing.com) | [`yahoo`](https://developer.yahoo.com) | [`yammer`](https://developer.yammer.com) | [`yandex`](https://tech.yandex.com) | [`zendesk`](https://developer.zendesk.com)
[`23andme`](https://api.23andme.com) | [`500px`](http://developers.500px.com) | [`acton`](https://developer.act-on.com) | [`amazon`](http://login.amazon.com/documentation) | [`angellist`](https://angel.co/api) | [`appnet`](https://developers.app.net) | [`asana`](https://asana.com/developers) | [`assembla`](http://api-doc.assembla.com) | [`basecamp`](https://github.com/basecamp/bcx-api) | [`beatport`](https://oauth-api.beatport.com) | [`beatsmusic`](https://developer.beatsmusic.com) | [`bitbucket`](https://confluence.atlassian.com/display/BITBUCKET) | [`bitly`](http://dev.bitly.com) | [`box`](https://developers.box.com) | [`buffer`](https://dev.buffer.com) | [`campaignmonitor`](https://www.campaignmonitor.com/api) | [`cheddar`](https://cheddarapp.com/developer) | [`coinbase`](https://developers.coinbase.com) | [`constantcontact`](https://developer.constantcontact.com) | [`copy`](https://developers.copy.com) | [`coursera`](https://tech.coursera.org) | [`dailymile`](http://www.dailymile.com/api/documentation) | [`dailymotion`](https://developer.dailymotion.com) | [`deezer`](http://developers.deezer.com) | [`delivery`](https://developers.delivery.com) | [`deviantart`](https://www.deviantart.com/developers/) | [`digitalocean`](https://developers.digitalocean.com) | [`discogs`](http://www.discogs.com/developers) | [`disqus`](https://disqus.com/api/docs) | [`dribbble`](http://developer.dribbble.com) | [`dropbox`](https://www.dropbox.com/developers) | [`echosign`](https://secure.echosign.com/public/docs/restapi/v3) | [`edmodo`](https://developers.edmodo.com) | [`elance`](https://www.elance.com/q/api2) | [`etsy`](https://www.etsy.com/developers) | [`eventbrite`](http://developer.eventbrite.com) | [`evernote`](https://dev.evernote.com) | [`everyplay`](https://developers.everyplay.com) | [`eyeem`](https://www.eyeem.com/developers) | [`facebook`](https://developers.facebook.com) | [`familysearch`](https://familysearch.org/developers) | [`feedly`](https://developer.feedly.com) | [`fitbit`](http://dev.fitbit.com) | [`flattr`](http://developers.flattr.net) | [`flickr`](https://www.flickr.com/services) | [`flowdock`](https://www.flowdock.com/api) | [`foursquare`](https://developer.foursquare.com) | [`freshbooks`](https://www.freshbooks.com/developers) | [`geeklist`](http://hackers.geekli.st) | [`getpocket`](http://getpocket.com/developer) | [`github`](https://developer.github.com) | [`gitlab`](http://doc.gitlab.com/ce/api) | [`gitter`](https://developer.gitter.im) | [`goodreads`](https://www.goodreads.com/api) | [`google`](https://developers.google.com) | [`harvest`](https://github.com/harvesthq/api) | [`heroku`](https://devcenter.heroku.com/categories/platform-api) | [`imgur`](https://api.imgur.com) | [`instagram`](https://instagram.com/developer) | [`jawbone`](https://jawbone.com/up/developer) | [`kakao`](https://developers.kakao.com) | [`linkedin`](https://developer.linkedin.com) | [`live`](https://msdn.microsoft.com/en-us/library/dn783283.aspx) | [`mailchimp`](https://apidocs.mailchimp.com) | [`mapmyfitness`](https://developer.underarmour.com) | [`meetup`](http://www.meetup.com/meetup_api) | [`mixcloud`](https://www.mixcloud.com/developers) | [`moves`](https://dev.moves-app.com) | [`myob`](http://developer.myob.com) | [`odesk`](https://developers.odesk.com) | [`openstreetmap`](http://wiki.openstreetmap.org/wiki/API_v0.6) | [`paypal`](https://developer.paypal.com) | [`plurk`](http://www.plurk.com/API) | [`podio`](https://developers.podio.com) | [`pushbullet`](https://docs.pushbullet.com/) | [`ravelry`](http://www.ravelry.com/api) | [`rdio`](http://www.rdio.com/developers) | [`redbooth`](https://redbooth.com/api) | [`reddit`](http://www.reddit.com/dev/api) | [`runkeeper`](http://developer.runkeeper.com) | [`salesforce`](https://developer.salesforce.com) | [`shoeboxed`](https://github.com/Shoeboxed/api) | [`shopify`](https://docs.shopify.com/api) | [`skyrock`](http://www.skyrock.com/developer) | [`slack`](https://api.slack.com) | [`slice`](https://developer.slice.com) | [`socrata`](http://dev.socrata.com) | [`soundcloud`](https://developers.soundcloud.com) | [`spotify`](https://developer.spotify.com) | [`square`](https://connect.squareup.com) | [`stackexchange`](https://api.stackexchange.com) | [`stocktwits`](http://stocktwits.com/developers) | [`stormz`](http://developer.stormz.me) | [`strava`](http://strava.github.io/api) | [`stripe`](https://stripe.com/docs) | [`surveygizmo`](http://apihelp.surveygizmo.com) | [`surveymonkey`](https://developer.surveymonkey.com) | [`thingiverse`](http://www.thingiverse.com/developers) | [`todoist`](https://developer.todoist.com) | [`trakt`](http://docs.trakt.apiary.io) | [`traxo`](https://developer.traxo.com) | [`trello`](https://trello.com/docs) | [`tripit`](https://www.tripit.com/developer) | [`tumblr`](https://www.tumblr.com/docs/en/api/v2) | [`twitch`](http://dev.twitch.tv) | [`twitter`](https://dev.twitter.com) | [`uber`](https://developer.uber.com) | [`underarmour`](https://developer.underarmour.com) | [`upwork`](https://developers.upwork.com) | [`uservoice`](https://developer.uservoice.com) | [`vend`](https://developers.vendhq.com) | [`vimeo`](https://developer.vimeo.com) | [`vk`](http://vk.com/dev) | [`weibo`](http://open.weibo.com) | [`withings`](http://oauth.withings.com/api) | [`wordpress`](https://developer.wordpress.com) | [`wrike`](https://developers.wrike.com) | [`xing`](https://dev.xing.com) | [`yahoo`](https://developer.yahoo.com) | [`yammer`](https://developer.yammer.com) | [`yandex`](https://tech.yandex.com) | [`zendesk`](https://developer.zendesk.com)

@@ -16,3 +16,3 @@

- [Providers][grant]
- Middleware
- **Middleware**
- [Express][express]

@@ -22,7 +22,8 @@ - [Koa][koa]

- [Reserved Routes for Grant][reserved-routes-for-grant]
- Configuration
- **Configuration**
- [Basics][configuration]
- [Redirect Url][redirect-url]
- [Redirect URL][redirect-url]
- [Static Overrides][static-overrides]
- [Dynamic Override][dynamic-override]
- **Advanced Configuration**
- [Custom Parameters][custom-parameters]

@@ -32,7 +33,9 @@ - [Custom Providers][custom-providers]

- [Programmatic Access][programmatic-access]
- [Response Data][response-data]
- [Sandbox Redirect URI][sandbox-redirect-uri]
- [Quirks][quirks]
- **[Response Data][response-data]**
- Misc
- [Typical Flow][typical-flow]
- [Get User Profile][get-user-profile]
- [Examples][examples]
- [Examples][grant-examples]

@@ -135,3 +138,2 @@

"scope": ["scope1", "scope2", ...],
"state": "some state",
"callback": "/provider1/callback"

@@ -149,3 +151,3 @@ },

- **transport** - transport to use to deliver the response data in your final callback `querystring` | `session` _(defaults to querystring if omitted)_
- **state** - generate 6 digit random state number on each authorization attempt `true` | `false` _(OAuth2 only, defaults to false if omitted)_
- **state** - generate random state string on each authorization attempt `true` | `false` _(OAuth2 only, defaults to false if omitted)_
- **provider1** - any [supported provider][grant] `facebook` | `twitter` ...

@@ -155,4 +157,4 @@ - **key** - `consumer_key` or `client_id` of your app

- **scope** - array of OAuth scopes to request
- **state** - OAuth state string to send
- **callback** - specific callback to use for this provider _(overrides the global one specified under the `server` key)_
- **custom_params** - custom authorization parameters _(see the [Custom Parameters][custom-parameters] section)_

@@ -162,5 +164,5 @@ _(additionally any of the [reserved keys][reserved-keys] can be overriden for a provider)_

## Redirect Url
## Redirect URL
For `callback/redirect` url of your OAuth application you should **always** use this format
For `redirect` URL of your OAuth application you should **always** use this format:

@@ -171,5 +173,5 @@ ```

Where `protocol` and `host` should match the ones from which you initiate the OAuth flow, and `provider` is the provider's name from the list of [supported providers][grant]
Where `protocol` and `host` should match the ones from which you initiate the OAuth flow, and `provider` is the provider's name from the list of [supported providers][grant].
This `redirect` url is used internally by Grant. You will receive the [response data][response-data] from the OAuth flow in the route specified in the `callback` key of your Grant configuration
This `redirect` URL is used internally by Grant. You will receive the [response data][response-data] from the OAuth flow in the route specified in the `callback` key of your Grant configuration.

@@ -179,3 +181,3 @@

You can add arbitrary _{object}_ keys inside your provider's configuration to create sub configurations that override the _global_ settings for that provider
You can add arbitrary `{object}` keys inside your provider's configuration to create sub configurations that override the _global_ settings for that provider:

@@ -211,3 +213,3 @@ ```js

Additionally you can make a `POST` request to the `/connect/:provider/:override?` route to override your provider's configuration dynamically on each request
Additionally you can make a `POST` request to the `/connect/:provider/:override?` route to override your provider's configuration dynamically on each request:

@@ -223,3 +225,3 @@ ```html

Keep in mind that in this case you'll have to mount the `body-parser` middleware for `express` or `koa` before mounting grant
Keep in mind that in this case you'll have to mount the `body-parser` middleware for Express or Koa before mounting Grant:

@@ -237,3 +239,3 @@ ```js

Alternatively you can use a `GET` request with the `/connect/:provider/:override?` route
Alternatively you can make a `GET` request to the `/connect/:provider/:override?` route:

@@ -251,18 +253,26 @@ ```js

- Some providers may employ custom authorization parameters outside of the ones specified in the [configuration][configuration] section. You can pass those custom parameters directly in your configuration, for example: Google - `access_type:'offline'`, Reddit - `duration:'permanent'`, Trello - `expiration:'never'`, and so on. Refer to the provider's OAuth documentation, and the Grant's [OAuth configuration][oauth-config] (search for `custom_parameters`)
Some providers may employ custom authorization parameters outside of the ones specified in the [configuration][configuration] section. You can pass those custom parameters using the `custom_params` option:
- Some providers require you to set your company name as a subdomain in the authorization urls. For example for Freshbooks, Shopify, Vend and Zendesk you can set that value through the `subdomain` option (alternatively you can override the entire `request_url`, `authorize_url` and `access_url` in your configuration)
```js
"google": {
"custom_params": {"access_type":"offline"}
},
"reddit": {
"custom_params": {"duration":"permanent"}
},
"trello": {
"custom_params": {"name":"my app", "expiration":"never"}
}
```
- Some providers may have a _sandbox_ urls for testing. To use them just override the entire `request_url`, `authorize_url` and `access_url` in your configuration
> Additionally any custom parameter that is not a [reserved][reserved-keys] key, and is listed under the `custom_parameters` array for that provider, can be defined along with the rest of the options.
- For SurveyMonkey set your Mashery user name as `key` and your application key as `api_key`
Refer to the provider's OAuth documentation, and the Grant's [OAuth configuration][oauth-config] *(search for `custom_parameters`)*.
- To use the LinkedIn's OAuth2 flow you should use `linkedin2` as a provider name, instead of `linkedin` which is for OAuth1
## Custom Providers
In case you have a private OAuth provider that you don't want to be part of the [officially supported][oauth-config] ones, you can still define it in your configuration by adding a custom key for it
In case you have a private OAuth provider that you don't want to be part of the [officially supported][oauth-config] ones, you can define it in your configuration by adding a custom key for it.
In this case you have to provide all of the required provider keys by yourself. Take a look at the [OAuth configuration][oauth-config] to see how the different types of flows are configured
In this case you have to specify all of the required provider keys by yourself:

@@ -275,8 +285,8 @@ ```js

},
"custom1": {
"mywebsite": {
"authorize_url": "https://mywebsite.com/authorize",
"access_url": "https://mywebsite.com/token",
"oauth": 2,
"key": "client_id",
"secret": "client_secret",
"key": "[CLIENT_ID]",
"secret": "[CLIENT_SECRET]",
"scope": ["read", "write"]

@@ -287,6 +297,8 @@ }

Take a look at the [OAuth configuration][oauth-config] to see how various providers are configured.
## Development Environments
You can easily configure different development environments
You can easily configure different development environments:

@@ -322,3 +334,3 @@ ```js

Then you can pass the environment flag
Then you can pass the environment flag:

@@ -329,3 +341,3 @@ ```bash

And use it in your application
And use it in your application:

@@ -340,3 +352,3 @@ ```js

Once you initialize a new instance of Grant
Once you initialize a new instance of Grant:

@@ -347,16 +359,123 @@ ```js

You get a special `config` _(`register.config` for Hapi)_ property attached to that instance. It contains the generated configuration data for all of the providers defined in your config file
You get a special `config` _(`register.config` for Hapi)_ property attached to that instance. It contains the generated configuration data for all of the providers defined in your config file.
> In case of dynamic access to a non pre-configured provider, it's automatically added to the `config` list on first access to the `/connect/:provider` route
> In case of dynamic access to a non pre-configured provider, it is automatically added to the `config` list on first access to the `/connect/:provider` route.
There is a `_config` property attached as well, which contains the data from the [config/oauth.json][oauth-config] file as well as all of the configuration methods used internally by Grant
There is a `_config` property attached as well, which contains the data from the [config/oauth.json][oauth-config] file as well as all of the configuration methods used internally by Grant.
> Typically you don't want to use the `_config` property directly. Also note that changes made to the `config` property are per Grant instance, where changes to the `_config` property are global
> Typically you don't want to use the `_config` property directly. Also note that changes made to the `config` property are per Grant instance, where changes to the `_config` property are global.
## Sandbox Redirect URI
Very rarely you may need to override the default `redirect_uri` that Grant generates for you.
For example Feedly supports only `http://localhost` as redirect URL of their Sandbox OAuth application, and it won't allow the `http://localhost/connect/feedly/callback` path:
```js
"feedly": {
"redirect_uri": "http://localhost"
}
```
In case you override the `redirect_uri` in your config, you'll have to redirect the user to the `[protocol]://[host]/connect/[provider]/callback` route that Grant uses to execute the last step of the OAuth flow:
```js
var qs = require('querystring')
app.get('/', function (req, res) {
if (process.env.NODE_ENV == 'development' &&
req.session.grant &&
req.session.grant.provider == 'feedly' &&
req.query.code
) {
res.redirect('/connect/' + req.session.grant.provider + '/callback?'
+ qs.stringify(req.query))
}
})
```
After that you will receive the results from the OAuth flow inside the route specified in the `callback` key of your configuration.
## Quirks
##### Subdomain
Some providers require you to set your company name as a *subdomain* in the OAuth URLs. For example for Freshbooks, Shopify, Vend and Zendesk you can set that value through the `subdomain` option:
```js
"shopify": {
"subdomain": "mycompany"
}
```
Then Grant will generate the correct OAuth URLs:
```js
"authorize_url": "https://mycompany.myshopify.com/admin/oauth/authorize",
"access_url": "https://mycompany.myshopify.com/admin/oauth/access_token"
```
> Alternatively you can override the entire `request_url`, `authorize_url` and `access_url` in your configuration.
##### Sandbox URLs
Some providers may have _sandbox_ URLs for testing. To use them just override the entire `request_url`, `authorize_url` and `access_url` in your configuration *(notice the `sandbox` bits)*:
```js
"paypal": {
"authorize_url": "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
"access_url": "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice"
},
"evernote": {
"request_url": "https://sandbox.evernote.com/oauth",
"authorize_url": "https://sandbox.evernote.com/OAuth.action",
"access_url": "https://sandbox.evernote.com/oauth"
}
```
##### Flickr
Flickr uses a custom authorization parameter to pass its scopes called `perms`. However you should use the regular `scope` option in your configuration:
```js
"flickr": {
"scope": ["write"]
}
```
##### SurveyMonkey
For SurveyMonkey set your Mashery user name as `key` and your application key as `api_key`:
```js
"surveymonkey": {
"key": "[MASHERY_USER_NAME]",
"secret": "[CLIENT_SECRET]",
"api_key": "[CLIENT_ID]"
}
```
##### LinkedIn
To use the LinkedIn's OAuth2 flow you should use `linkedin2` as provider name, instead of `linkedin` which is for OAuth1:
```js
"linkedin2": {
// then navigate to /connect/linkedin2
}
```
## Response Data
The OAuth response data is returned as a querystring in your **final** callback - the one you specify in the `callback` key of your Grant configuration
The OAuth response data is returned as a querystring in your **final** callback - the one you specify in the `callback` key of your Grant configuration.
Alternatively the response data can be returned in the session, see the [configuration][configuration] section above and the [session transport][session-transport-example] example
Alternatively the response data can be returned in the session, see the [configuration][configuration] section above and the [session transport][session-transport-example] example.

@@ -366,3 +485,3 @@

For OAuth1 the `access_token` and the `access_secret` are accessible directly, `raw` contains the raw response data
For OAuth1 the `access_token` and the `access_secret` are accessible directly, `raw` contains the raw response data:

@@ -384,3 +503,3 @@ ```js

For OAuth2 the `access_token` and the `refresh_token` (if present) are accessible directly, `raw` contains the raw response data
For OAuth2 the `access_token` and the `refresh_token` (if present) are accessible directly, `raw` contains the raw response data:

@@ -402,3 +521,3 @@ ```js

In case of an error, the `error` key will be populated with the raw error data
In case of an error, the `error` key will be populated with the raw error data:

@@ -416,6 +535,6 @@ ```js

1. Register OAuth application on your provider's web site
2. For `callback/redirect` url of your OAuth application **always** use this format
1. Register OAuth application on your provider's web site.
2. For `redirect` URL of your OAuth application **always** use this format:
`[protocol]://[host]/connect/[provider]/callback`
3. Create a `config.json` file containing
3. Create a `config.json` file containing:

@@ -428,4 +547,4 @@ ```js

"facebook": {
"key": "[APP_ID]",
"secret": "[APP_SECRET]",
"key": "[CLIENT_ID]",
"secret": "[CLIENT_SECRET]",
"callback": "/handle_facebook_response"

@@ -439,3 +558,3 @@ },

```
4. Initialize Grant and mount it
4. Initialize Grant and mount it:

@@ -451,9 +570,9 @@ ```js

app.use(grant)
// or Koa (see above)
// or Hapi (see above)
// or Koa
// or Hapi
```
5. Navigate to `/connect/facebook` to initiate the OAuth flow for Facebook, or navigate to `/connect/twitter` to initiate the OAuth flow for Twitter
6. Once the OAuth flow is completed you will receive the response data in the `/handle_facebook_response` route for Facebook, and in the `/handle_twitter_response` route for Twitter
5. Navigate to `/connect/facebook` to initiate the OAuth flow for Facebook, or navigate to `/connect/twitter` to initiate the OAuth flow for Twitter.
6. Once the OAuth flow is completed you will receive the response data in the `/handle_facebook_response` route for Facebook, and in the `/handle_twitter_response` route for Twitter.
_(also take a look at the [examples][examples])_
_(also take a look at the [examples][grant-examples])_

@@ -463,5 +582,5 @@

Once you have your access tokens secured, you can start making authorized requests on behalf of your users. _**[Purest][purest]**_ is a great REST API library that supports **dozens** of REST API providers
Once you have your access tokens secured, you can start making authorized requests on behalf of your users. **[Purest][purest]** is a generic REST API library that supports **hundreds** of REST API providers.
For example, you may want to get the user's profile after the OAuth flow has completed
For example, you may want to get the user's profile after the OAuth flow has completed:

@@ -492,3 +611,5 @@ ```js

> Full list of all providers and how to get their *user profile* endpoint can be found [here][purest-user].
## License

@@ -499,5 +620,5 @@

[playground]: https://grant-oauth.herokuapp.com/
[purest]: https://github.com/simov/purest
[request]: https://github.com/request/request
[npm-version]: http://img.shields.io/npm/v/grant.svg?style=flat-square (NPM Version)
[travis-ci]: https://img.shields.io/travis/simov/grant/master.svg?style=flat-square (Build Status)
[coveralls-status]: https://img.shields.io/coveralls/simov/grant.svg?style=flat-square (Test Coverage)

@@ -508,10 +629,10 @@ [npm]: https://www.npmjs.org/package/grant

[npm-version]: http://img.shields.io/npm/v/grant.svg?style=flat-square (NPM Version)
[travis-ci]: https://img.shields.io/travis/simov/grant/master.svg?style=flat-square (Build Status)
[coveralls-status]: https://img.shields.io/coveralls/simov/grant.svg?style=flat-square (Test Coverage)
[grant-oauth]: https://grant-oauth.herokuapp.com
[purest]: https://github.com/simov/purest
[purest-user]: https://github.com/simov/purest/blob/master/test/request/get.js
[request]: https://github.com/request/request
[oauth-config]: https://github.com/simov/grant/blob/master/config/oauth.json
[reserved-keys]: https://github.com/simov/grant/blob/master/config/reserved.json
[examples]: https://github.com/simov/grant/tree/master/example
[grant-examples]: https://github.com/simov/grant/tree/master/example
[session-transport-example]: https://github.com/simov/grant/blob/master/example/session-transport/app.js

@@ -533,4 +654,6 @@

[programmatic-access]: #programmatic-access
[sandbox-redirect-uri]: #sandbox-redirect-uri
[quirks]: #quirks
[response-data]: #response-data
[typical-flow]: #typical-flow
[get-user-profile]: #get-user-profile
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc