grant

OAuth Middleware for Express, Koa and Hapi

Grant

100+ Supported Providers / OAuth Playground

23andme | 500px | acton | amazon | angellist | appnet | asana | assembla | basecamp | beatport | beatsmusic | bitbucket | bitly | box | buffer | campaignmonitor | cheddar | coinbase | constantcontact | copy | coursera | dailymile | dailymotion | deezer | delivery | deviantart | digitalocean | discogs | disqus | dribbble | dropbox | echosign | edmodo | elance | etsy | eventbrite | evernote | everyplay | eyeem | facebook | familysearch | feedly | fitbit | flattr | flickr | flowdock | foursquare | freshbooks | geeklist | getpocket | github | gitlab | gitter | goodreads | google | harvest | heroku | imgur | instagram | jawbone | kakao | linkedin | live | mailchimp | mapmyfitness | meetup | mixcloud | moves | myob | odesk | openstreetmap | paypal | plurk | podio | rdio | redbooth | reddit | runkeeper | salesforce | shoeboxed | shopify | skyrock | slack | slice | socrata | soundcloud | spotify | square | stackexchange | stocktwits | stormz | strava | stripe | surveygizmo | surveymonkey | thingiverse | trakt | traxo | trello | tripit | tumblr | twitch | twitter | uber | underarmour | upwork | uservoice | vend | vimeo | vk | withings | wordpress | xing | yahoo | yammer | yandex | zendesk

npm install grant-express
var express = require('express')
  , session = require('express-session')
var Grant = require('grant-express')
  , grant = new Grant({/*configuration - see below*/})
 
var app = express()
// REQUIRED: (any session store - see ./example/express-session) 
app.use(session({secret:'grant'}))
// mount grant 
app.use(grant)
npm install grant-koa
var koa = require('koa')
  , session = require('koa-session')
  , mount = require('koa-mount')
var Grant = require('grant-koa')
  , grant = new Grant({/*configuration - see below*/})
 
var app = koa()
// REQUIRED: (any session store - see ./example/koa-session) 
app.keys = ['grant']
app.use(session(app))
// mount grant 
app.use(mount(grant))
npm install grant-hapi
var Hapi = require('hapi')
  , yar = require('yar')
var Grant = require('grant-hapi')
  , grant = new Grant()
 
var server = new Hapi.Server()
server.register([
  // REQUIRED: 
  {
    register: yar,
    options: {cookieOptions: {password:'grant', isSecure:false}}
  },
  // register grant 
  {
    register: grant,
    options: {/*configuration - see below*/}
  }
], function (err) {
  server.start()
})
/connect/:provider/:override?
/connect/:provider/callback
{
  "server": {
    "protocol": "http",
    "host": "localhost:3000",
    "callback": "/callback",
    "transport": "session",
    "state": true
  },
  "provider1": {
    "key": "...",
    "secret": "...",
    "scope": ["scope1", "scope2", ...],
    "state": "some state",
    "callback": "/provider1/callback"
  },
  "provider2": {...},
  ...
}
  • server - configuration about your server
    • protocol - either http or https
    • host - your server's host name localhost:3000 | dummy.com:5000 | mysite.com ...
    • callback - common callback for all providers in your config /callback | /done ...
    • 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)
  • provider1 - any supported provider facebook | twitter ...
    • key - consumer_key or client_id of your app
    • secret - consumer_secret or client_secret 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)

(additionally any of the reserved keys can be overriden for a provider)

For callback/redirect url of your OAuth application you should always use this format

[protocol]://[host]/connect/[provider]/callback

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

This redirect url is used internally by Grant. You will receive the response data from the OAuth flow in the route specified in the callback key of your Grant configuration

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

// navigate to /connect/facebook 
"facebook": {
  "key": "...",
  "secret": "...",
  // by default request publish permissions 
  "scope": ["publish_actions", "publish_stream"],
  // set specific callback route on your server for this provider 
  "callback": "/facebook/callback",
  // navigate to /connect/facebook/groups 
  "groups": {
    // request only group permissions 
    "scope": ["user_groups", "friends_groups"]
  },
  // navigate to /connect/facebook/pages 
  "pages": {
    // request only page permissions 
    "scope": ["manage_pages"],
    // additionally use specific callback route on your server for this override 
    "callback": "/facebook_pages/callback"
  }
}

(the custom key names cannot be one of the reserved keys)

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

<form action="/connect/facebook" method="post" accept-charset="utf-8">
  <input name="state" type="text" value="" />
  <input name="scope" type="checkbox" value="user_groups" />
  <input name="scope" type="checkbox" value="manage_pages" />
  <button>submit</button>
</form>

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

// express 
var bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({extended:true}))
app.use(grant)
// koa 
var bodyParser = require('koa-bodyparser')
app.use(bodyParser())
app.use(mount(grant))

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

app.get('/connect_facebook', function (reqres) {
  // generate random state parameter on each authorization attempt 
  var state = (Math.floor(Math.random() * 999999) + 1)
  res.redirect('/connect/facebook?state=' + state)
})
  • Some providers may employ custom authorization parameters outside of the ones specified in the 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 (search for custom_parameters)

  • 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)

  • 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

  • For SurveyMonkey set your Mashery user name as key and your application key as api_key

  • To use the LinkedIn's OAuth2 flow you should use linkedin2 as a provider name, instead of linkedin which is for OAuth1

In case you have a private OAuth provider that you don't want to be part of the officially supported ones, you can still 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 to see how the different types of flows are configured

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

You can easily configure different development environments

{
  "development": {
    "server": {"protocol": "http", "host": "dummy.com:3000"},
    "facebook": {
      "key": "development OAuth app credentials",
      "secret": "development OAuth app credentials"
    },
    "twitter": {...}, ...
  },
  "staging": {
    "server": {"protocol": "https", "host": "staging.mywebsite.com"},
    "facebook": {
      "key": "staging OAuth app credentials",
      "secret": "staging OAuth app credentials"
    },
    "twitter": {...}, ...
  },
  "production": {
    "server": {"protocol": "https", "host": "mywebsite.com"},
    "facebook": {
      "key": "production OAuth app credentials",
      "secret": "production OAuth app credentials"
    },
    "twitter": {...}, ...
  }
}

Then you can pass the environment flag

NODE_ENV=production node app.js

And use it in your application

var config = require('./config.json')
var grant = new Grant(config[process.env.NODE_ENV||'development'])

Once you initialize a new instance of Grant

var grant = new Grant(require('./config'))

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

There is a _config property attached as well, which contains the data from the config/oauth.json 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

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 section above and the session transport example

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

{
  access_token:'...',
  access_secret:'...',
  raw:{
    oauth_token:'...',
    oauth_token_secret:'...',
    some:'other data'
  }
}

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

{
  access_token:'...',
  refresh_token:'...',
  raw:{
    access_token:'...',
    refresh_token:'...',
    some:'other data'
  }
}

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

{
  error:{
    some:'error data'
  }
}
  1. Register OAuth application on your provider's web site
  2. For callback/redirect url of your OAuth application always use this format [protocol]://[host]/connect/[provider]/callback
  3. Create a config.json file containing
"server": {
  "protocol": "https",
  "host": "mywebsite.com"
},
"facebook": {
  "key": "[APP_ID]",
  "secret": "[APP_SECRET]",
  "callback": "/handle_facebook_response"
},
"twitter": {
  "key": "[CONSUMER_KEY]",
  "secret": "[CONSUMER_SECRET]",
  "callback": "/handle_twitter_response"
}
  1. Initialize Grant and mount it
// Express 
var express = require('express')
  , session = require('express-session')
var Grant = require('grant-express')
  , grant = new Grant(require('./config.json'))
var app = express()
app.use(session({secret:'grant'}))
app.use(grant)
// or Koa (see above) 
// or Hapi (see above) 
  1. Navigate to /connect/facebook to initiate the OAuth flow for Facebook, or navigate to /connect/twitter to initiate the OAuth flow for Twitter
  2. 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)

Once you have your access tokens secured, you can start making authorized requests on behalf of your users. Purest is a great REST API library that supports dozens of REST API providers

For example, you may want to get the user's profile after the OAuth flow has completed

var Purest = require('purest')
  , facebook = new Purest({provider:'facebook'})
  , twitter = new Purest({provider:'twitter',
    key:'[CONSUMER_KEY]', secret:'[CONSUMER_SECRET]'})
 
facebook.query()
  .get('me')
  .auth('[ACCESS_TOKEN]')
  .request(function (errresbody) {
    // here body is a parsed JSON object containing 
    // id, first_name, last_name, gender, username, ... 
  })
 
twitter.query()
  .get('users/show')
  .qs({screen_name:'nodejs'})
  .auth('[ACCESS_TOKEN]', '[ACCESS_SECRET]')
  .request(function (errresbody) {
    // here body is a parsed JSON object containing 
    // id, screen_name, ... 
  })

MIT