
# Grant

[![npm-version]][npm] [![travis-ci]][travis] [![coveralls-status]][coveralls]


## 180+ Supported Providers / [OAuth Playground][grant-oauth]

[`23andme`](https://api.23andme.com) | [`500px`](https://github.com/500px/api-documentation) | [`acton`](https://developer.act-on.com) | [`acuityscheduling`](https://developers.acuityscheduling.com) | [`aha`](https://www.aha.io/api) | [`amazon`](https://login.amazon.com/documentation) | [`angellist`](https://angel.co/api) | [`arcgis`](https://developers.arcgis.com) | [`asana`](https://asana.com/developers) | [`assembla`](https://api-docs.assembla.cc) | [`atlassian`](https://developer.atlassian.com) | [`auth0`](https://auth0.com/docs) | [`authentiq`](https://www.authentiq.com/developers) | [`aweber`](https://api.aweber.com) | [`axosoft`](https://developer.axosoft.com) | [`baidu`](https://developer.baidu.com) | [`basecamp`](https://github.com/basecamp/bc3-api) | [`battlenet`](https://develop.battle.net) | [`beatport`](https://oauth-api.beatport.com) | [`bitbucket`](https://developer.atlassian.com/bitbucket/api/2/reference/) | [`bitly`](https://dev.bitly.com) | [`box`](https://developer.box.com) | [`buffer`](https://buffer.com/developers) | [`campaignmonitor`](https://www.campaignmonitor.com/api) | [`cheddar`](https://cheddarapp.com/developer) | [`clio`](https://app.clio.com/api/v4/documentation) | [`coinbase`](https://developers.coinbase.com) | [`concur`](https://developer.concur.com) | [`constantcontact`](https://developer.constantcontact.com) | [`coursera`](https://building.coursera.org) | [`dailymotion`](https://developer.dailymotion.com) | [`deezer`](https://developers.deezer.com) | [`delivery`](https://developers.delivery.com) | [`deputy`](https://www.deputy.com/api-doc/) | [`deviantart`](https://www.deviantart.com/developers/) | [`digitalocean`](https://developers.digitalocean.com) | [`discogs`](https://www.discogs.com/developers/) | [`discord`](https://discordapp.com/developers/docs/intro) | [`disqus`](https://disqus.com/api/docs) | [`docusign`](https://developers.docusign.com) | [`dribbble`](https://developer.dribbble.com) | [`dropbox`](https://www.dropbox.com/developers) | [`ebay`](https://developer.ebay.com) | [`echosign`](https://secure.echosign.com/public/docs/restapi/v3) | [`ecwid`](https://developers.ecwid.com) | [`edmodo`](https://partnerships.edmodo.com) | [`egnyte`](https://developers.egnyte.com) | [`etsy`](https://www.etsy.com/developers) | [`eventbrite`](https://www.eventbrite.com/platform) | [`evernote`](https://dev.evernote.com) | [`eyeem`](https://github.com/eyeem/Public-API) | [`facebook`](https://developers.facebook.com) | [`familysearch`](https://www.familysearch.org/developers/) | [`feedly`](https://developer.feedly.com) | [`fitbit`](https://dev.fitbit.com) | [`flattr`](http://developers.flattr.net) | [`flickr`](https://www.flickr.com/services) | [`flowdock`](https://www.flowdock.com/api) | [`formstack`](https://developers.formstack.com) | [`foursquare`](https://developer.foursquare.com) | [`freeagent`](https://dev.freeagent.com) | [`freelancer`](https://developers.freelancer.com) | [`freshbooks`](https://www.freshbooks.com/developers) | [`geeklist`](http://hackers.geekli.st) | [`genius`](https://docs.genius.com) | [`getbase`](https://developers.getbase.com) | [`getpocket`](https://getpocket.com/developer) | [`gitbook`](https://developer.gitbook.com) | [`github`](https://developer.github.com) | [`gitlab`](https://docs.gitlab.com/ce/api/) | [`gitter`](https://developer.gitter.im) | [`goodreads`](https://www.goodreads.com/api) | [`google`](https://developers.google.com) | [`groove`](https://www.groovehq.com/docs) | [`gumroad`](https://gumroad.com/api) | [`harvest`](https://help.getharvest.com/api-v2/) | [`hellosign`](https://www.hellosign.com/api) | [`heroku`](https://devcenter.heroku.com/categories/platform-api) | [`homeaway`](https://www.homeaway.com/platform) | [`hootsuite`](https://developer.hootsuite.com) | [`ibm`](https://www.ibm.com/support/knowledgecenter/SSAT72/com.ibm.help.ssm_adminguide.doc/t_RegisteringforblueID.html) | [`iconfinder`](https://developer.iconfinder.com) | [`idme`](https://developer.id.me) | [`idonethis`](https://i-done-this.readme.io/docs) | [`imgur`](https://apidocs.imgur.com) | [`infusionsoft`](https://developer.infusionsoft.com) | [`instagram`](https://instagram.com/developer) | [`intuit`](https://developer.intuit.com) | [`jamendo`](https://devportal.jamendo.com/) | [`jumplead`](https://developer.jumplead.com) | [`kakao`](https://developers.kakao.com) | [`line`](https://developers.line.biz) | [`linkedin`](https://www.linkedin.com/developers) | [`live`](https://docs.microsoft.com/en-us/onedrive/developer/rest-api/getting-started/msa-oauth?view=odsp-graph-online) | [`livechat`](https://developers.livechatinc.com) | [`lyft`](https://developer.lyft.com) | [`mailchimp`](https://developer.mailchimp.com) | [`mailup`](http://help.mailup.com/display/mailupapi/REST+API) | [`mailxpert`](https://dev.mailxpert.ch) | [`mapmyfitness`](https://developer.underarmour.com) | [`mastodon`](https://docs.joinmastodon.org/) | [`medium`](https://developers.medium.com) | [`meetup`](https://www.meetup.com/meetup_api/) | [`mention`](https://dev.mention.com) | [`microsoft`](https://developer.microsoft.com/en-us/graph) | [`mixcloud`](https://www.mixcloud.com/developers) | [`mixer`](https://dev.mixer.com) | [`moxtra`](https://developer.moxtra.com) | [`myob`](https://developer.myob.com) | [`naver`](https://developers.naver.com) | [`nest`](https://developers.nest.com) | [`nokotime`](https://developer.nokotime.com) | [`nylas`](https://docs.nylas.com) | [`okta`](https://developer.okta.com/) | [`onelogin`](https://developers.onelogin.com) | [`openstreetmap`](https://wiki.openstreetmap.org/wiki/API_v0.6) | [`optimizely`](https://developers.optimizely.com) | [`patreon`](https://docs.patreon.com) | [`paypal`](https://developer.paypal.com) | [`phantauth`](https://www.phantauth.net) | [`pinterest`](https://developers.pinterest.com) | [`plurk`](https://www.plurk.com/API) | [`podio`](https://developers.podio.com) | [`producthunt`](https://api.producthunt.com/v2/docs) | [`projectplace`](https://service.projectplace.com/apidocs) | [`pushbullet`](https://docs.pushbullet.com) | [`qq`](https://wiki.connect.qq.com/%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C_oauth2-0) | [`ravelry`](https://www.ravelry.com/api) | [`redbooth`](https://redbooth.com/api) | [`reddit`](https://www.reddit.com/dev/api) | [`runkeeper`](https://runkeeper.com/developer/healthgraph/) | [`salesforce`](https://developer.salesforce.com) | [`shoeboxed`](https://github.com/Shoeboxed/api) | [`shopify`](https://developers.shopify.com) | [`skyrock`](https://www.skyrock.com/developer) | [`slack`](https://api.slack.com) | [`slice`](https://developer.slice.com) | [`smartsheet`](https://smartsheet-platform.github.io/api-docs) | [`smugmug`](https://api.smugmug.com) | [`snapchat`](https://kit.snapchat.com) | [`socialpilot`](https://developer.socialpilot.co) | [`socrata`](https://dev.socrata.com) | [`soundcloud`](https://developers.soundcloud.com) | [`spotify`](https://developer.spotify.com) | [`square`](https://squareup.com/developers) | [`stackexchange`](https://api.stackexchange.com) | [`stocktwits`](https://api.stocktwits.com/developers) | [`stormz`](https://developer.stormz.me) | [`strava`](https://developers.strava.com) | [`stripe`](https://stripe.com/docs) | [`surveygizmo`](https://apihelp.surveygizmo.com) | [`surveymonkey`](https://developer.surveymonkey.com) | [`thingiverse`](https://www.thingiverse.com/developers) | [`ticketbud`](https://api.ticketbud.com) | [`timelyapp`](https://dev.timelyapp.com) | [`todoist`](https://developer.todoist.com) | [`trakt`](https://trakt.docs.apiary.io) | [`traxo`](https://developer.traxo.com) | [`trello`](https://developers.trello.com) | [`tripit`](https://www.tripit.com/developer) | [`tumblr`](https://www.tumblr.com/docs/en/api/v2) | [`twitch`](https://dev.twitch.tv) | [`twitter`](https://developer.twitter.com) | [`typeform`](https://developer.typeform.com) | [`uber`](https://developer.uber.com) | [`underarmour`](https://developer.underarmour.com) | [`unsplash`](https://unsplash.com/documentation) | [`upwork`](https://developers.upwork.com) | [`uservoice`](https://developer.uservoice.com) | [`vend`](https://developers.vendhq.com) | [`venmo`](https://developers.braintreepayments.com/guides/venmo/overview/) | [`verticalresponse`](http://developers.verticalresponse.com) | [`viadeo`](https://partners.viadeo.com) | [`vimeo`](https://developer.vimeo.com) | [`visualstudio`](https://docs.microsoft.com/en-us/vsts/integrate/get-started/authentication/oauth?view=vsts) | [`vk`](https://vk.com/dev) | [`wechat`](https://mp.weixin.qq.com) | [`weekdone`](https://weekdone.com/developer) | [`weibo`](https://open.weibo.com) | [`withings`](http://developer.withings.com) | [`wordpress`](https://developer.wordpress.com) | [`wrike`](https://developers.wrike.com) | [`xero`](https://developer.xero.com) | [`xing`](https://dev.xing.com) | [`yahoo`](https://developer.yahoo.com) | [`yammer`](https://developer.yammer.com/docs) | [`yandex`](https://tech.yandex.com) | [`zeit`](https://zeit.co/docs) | [`zendesk`](https://developer.zendesk.com) | [`zoom`](https://marketplace.zoom.us/docs)


## Table of Contents

- **[Providers](#grant)**
- **Middlewares**
  - [Express](#express)
  - [Koa](#koa)
  - [Hapi](#hapi)
- **Basics**
  - [Configuration](#configuration)
  - [Reserved Routes](#reserved-routes)
  - [Redirect URL](#redirect-url)
  - [Path Prefix](#path-prefix)
  - [OpenID Connect](#openid-connect)
  - [Custom Parameters](#custom-parameters)
  - [Static Overrides](#static-overrides)
  - [Dynamic Override](#dynamic-override)
- **Response Data**
  - [Response Data Format](#response-data)
  - [Response Data Transport](#response-data-transport)
  - [Limit Response Data](#limit-response-data)
  - [Session](#session)
- **Advanced**
  - [All Available Options](#all-available-options)
  - [Configuration Scopes](#configuration-scopes)
  - [Custom Providers](#custom-providers)
  - [Development Environments](#development-environments)
  - [OAuth Proxy](#oauth-proxy)
  - [Redirect URI](#redirect-uri)
  - [Token Endpoint Auth Method](#token-endpoint-auth-method)
  - [Meta Configuration](#meta-configuration)
- **OAuth Quirks**
  - [Subdomain URLs](#subdomain-urls)
  - [Sandbox OAuth URLs](#sandbox-oauth-urls)
  - [Sandbox Redirect URI](#sandbox-redirect-uri)
  - [Provider Quirks](#provider-quirks)
- **Misc**
  - [Alternative Require](#alternative-require)
  - [Alternative Instantiation](#alternative-instantiation)
  - [Programmatic Access](#programmatic-access)
  - [Get User Profile](#get-user-profile)
- **[Examples][examples]**
- **[Changelog][changelog]**

----

# Middlewares


## Express

```bash
npm install grant-express
```

```js
var express = require('express')
var session = require('express-session')
var grant = require('grant-express')

var app = express()
// REQUIRED: any session store - see /examples/express-session-stores
app.use(session({secret: 'grant'}))
// mount grant
app.use(grant({/*configuration - see below*/}))
```


## Koa

```bash
npm install grant-koa
```

```js
var Koa = require('koa')
var session = require('koa-session')
var grant = require('grant-koa')

var app = new Koa()
// REQUIRED: any session store - see /examples/koa-session-stores
app.keys = ['grant']
app.use(session(app))
// mount grant
app.use(grant({/*configuration - see below*/}))
```


## Hapi

```bash
npm install grant-hapi
```

```js
var Hapi = require('hapi')
var yar = require('yar')
var grant = require('grant-hapi')

var server = new Hapi.Server()
server.register([
  // REQUIRED: any session store - see /examples/hapi-session-stores
  {plugin: yar, options: {cookieOptions: {password: 'grant', isSecure: false}}},
  // mount grant
  {plugin: grant(), options: {/*configuration - see below*/}}
])
```

---

# Basics


## Configuration

```json
{
  "defaults": {
    "protocol": "http",
    "host": "localhost:3000",
    "transport": "session",
    "state": true
  },
  "google": {
    "key": "...",
    "secret": "...",
    "scope": ["openid"],
    "nonce": true,
    "custom_params": {"access_type": "offline"},
    "callback": "/hello"
  },
  "twitter": {
    "key": "...",
    "secret": "...",
    "callback": "/hi"
  }
}
```

- **defaults** - default configuration for all providers *(previously this option was called `server`)*
  - **protocol** - either `http` or `https`
  - **host** - your server's host name `localhost:3000` | `dummy.com:5000` | `awesome.com` ...
  - **transport** - transport to use to deliver the [response data](#response-data) in your final callback route `querystring` | `session` *(defaults to querystring if omitted)*
  - **state** - generate random state string on each authorization attempt `true` | `false` *(OAuth2 only, defaults to false if omitted)*
- **provider** - any [supported provider](#grant) `google` | `twitter` ...
  - **key** - `consumer_key` or `client_id` of your OAuth app
  - **secret** - `consumer_secret` or `client_secret` of your OAuth app
  - **scope** - array of OAuth scopes to request
  - **nonce** - generate random nonce string on each authorization attempt `true` | `false` *([OpenID Connect](#openid-connect) only, defaults to false if omitted)*
  - **custom_params** - custom [authorization parameters](#custom-parameters)
  - **callback** - specific callback route to use for this provider only `/callback` | `/done` ...

> List of all [available options](#all-available-options).


## Reserved Routes

Grant operates on the following two routes:

```
/connect/:provider/:override?
/connect/:provider/callback
```

You login by navigating to the `/connect/:provider` route where `:provider` is a key in your configuration, usually one of the [officially supported](#grant) providers. Additionally you can login through a [static override](#static-overrides) key defined for that provider, in your configuration, by navigating to the `/connect/:provider/:override?` route.


## Redirect URL

You should **always** use the following format for the `redirect_uri` of your OAuth App:

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

The `protocol` and the `host` are the options you specify inside the `defaults` key of your [configuration](#configuration), and this is where your server accepts incoming requests. The `provider` is a key in your configuration, usually one of the [officially supported](#grant) providers.

Note that the `/connect/:provider/callback` route is used **internally** by Grant! You will receive the OAuth [response data](#response-data) inside the `callback` route specified in your configuration.


## Path Prefix

You can mount Grant under specific path prefix:

```js
// Express
app.use('/path/prefix', grant(config))
// Koa - using koa-mount
app.use(mount('/path/prefix', grant(config)))
// Hapi
server.register([{routes: {prefix: '/path/prefix'}, plugin: grant(config)}])
```

In this case it is **required** to specify that `path` prefix in your configuration:

```json
{
  "defaults": {
    "protocol": "...",
    "host": "...",
    "path": "/path/prefix"
  }
}
```

That path prefix **should** also be specified in your OAuth App [Redirect URL](#redirect-url):

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

Optionally you can prefix your `callback` routes as well:

```json
{
  "github": {
    "callback": "/path/prefix/hello"
  }
}
```


## OpenID Connect

The `nonce` option is **recommended** when requesting the `openid` scope:

```json
{
  "google": {
    "scope": ["openid"],
    "nonce": true
  }
}
```

Grant **does not** verify the signature of the returned `id_token` because that requires discovery, caching, and expiration of the provider's public keys.

However, Grant tries to decode the `id_token` and verifies the following two claims *(returns error respectively)*:

1. `aud` - is the token intended for my OAuth app?
2. `nonce` - does it tie to a request of my own?

For convenience the [response data](#response-data) contains the decoded `id_token`

> Take a look at the OpenID Connect [example][openid-connect-example].


## Custom Parameters

Some providers may employ custom authorization parameters, that you can configure using the `custom_params` option:

```json
{
  "google": {
    "custom_params": {"access_type": "offline"}
  },
  "reddit": {
    "custom_params": {"duration": "permanent"}
  },
  "trello": {
    "custom_params": {"name": "my app", "expiration": "never"}
  }
}
```


## Static Overrides

You can specify provider sub configurations using the `overrides` key:

```json
{
  "github": {
    "key": "...", "secret": "...",
    "scope": ["public_repo"],
    "callback": "/hello",
    "overrides": {
      "notifications": {
        "key": "...", "secret": "...",
        "scope": ["notifications"]
      },
      "all": {
        "scope": ["repo", "gist", "user"],
        "callback": "/awesome"
      }
    }
  }
}
```

Navigate to:

- `/connect/github` to request the *public_repo* `scope`
- `/connect/github/notifications` to request the _notifications_ `scope` using another OAuth App (`key` and `secret`)
- `/connect/github/all` to request a bunch of `scope`s and also receive the response data in another `callback` route


## Dynamic Override

In some cases you may want certain parts of your configuration to be set dynamically.

For example for `shopify` you have to embed the user's shop name into the OAuth URLs, so it makes sense to allow the [`subdomain` option](#subdomain-urls) to be set dynamically:

```json
{
  "shopify": {
    "dynamic": ["subdomain"]
  }
}
```

Then you can have a form on your website allowing the user to specify the shop name:

```html
<form action="/connect/shopify" method="POST" accept-charset="utf-8">
  <input type="text" name="subdomain" value="" />
  <button>Login</button>
</form>
```

Keep in mind that when making a `POST` request to the `/connect/:provider/:override?` route you have to mount the `body-parser` middleware for Express and Koa before mounting Grant:

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

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

```
https://awesome.com/connect/shopify?subdomain=usershop
```

Lastly you can use the request/response lifecycle state as well:

```js
// Express
res.locals.grant = {dynamic: {subdomain: 'usershop'}}
// Koa
ctx.state.grant = {dynamic: {subdomain: 'usershop'}}
// Hapi
request.plugins.grant = {dynamic: {subdomain: 'usershop'}}
```

Note that the dynamic overrides set in the request/response lifecycle state are not controlled by the `dynamic` configuration option.

Any allowed dynamic key sent through GET/POST request will override the identical one set in state.

---

# Response Data

The OAuth response data is returned in your **final** `callback` route.

- By default the OAuth response data will be encoded as **querystring**.

- You can instruct Grant to return the response data inside the **session** [instead](#response-data-transport) instead, by using the `transport` option.

- You can **limit** the amount of [returned data](#limit-response-data) by using the `response` option.

## OAuth 2.0

```js
{
  id_token: {header: {...}, payload: {...}, signature: '...'},
  access_token: '...',
  refresh_token: '...',
  raw: {
    id_token: '...',
    access_token: '...',
    refresh_token: '...',
    some: 'other data'
  }
}
```

> The `refresh_token` is optional. The `id_token` is returned only for [OpenID Connect](#openid-connect) providers requesting the _openid_ scope.


## OAuth 1.0a

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


## Error

```js
{
  error: {
    some: 'error data'
  }
}
```

> In case of an error, the `error` key will contain the error data.


## Response Data Transport

By default Grant will encode the OAuth [response data](#response-data) as *querystring* in your final `callback` route:

```json
{
  "github": {
    "callback": "/hello"
  }
}
```

This final `/hello?access_token=...` redirect potentially may leak private data in your server logs, especially if you are behind reverse proxy.

It is **recommended** to use the *session* `transport` instead:

```json
{
  "defaults": {
    "transport": "session"
  },
  "github": {
    "callback": "/hello"
  }
}
```

That way the result will no longer be encoded as *querystring*, and you will receive the response data inside the [*session*][session-transport-example] instead.

Lastly the request/response lifecycle state can be used as `state` transport:

```json
{
  "defaults": {
    "transport": "state"
  }
}
```

Note that in this case a `callback` route is not needed, and if there is one, the user won't be redirected there. The response data will be available in the request/response lifecycle state instead:

```js
// Express
res.locals.grant.response
// Koa
ctx.state.grant.response
// Hapi
request.plugins.grant.response
```


## Limit Response Data

By default Grant will return all available [response data](#response-data) in your final `callback` route:

```js
{
  id_token: {header: {...}, payload: {...}, signature: '...'},
  access_token: '...',
  refresh_token: '...',
  raw: {
    id_token: '...',
    access_token: '...',
    refresh_token: '...',
    some: 'other data'
  }
}
```

However, encoding _potentially_ large amounts of data as **querystring** can lead to incompatibility issues with some servers and browsers, and generally is considered a bad practice.

It can also cause problems when the **session** transport is used, and the particular session store implementation encodes the entire session in a cookie, not just the session ID. In this case some servers may reject the HTTP request because of too big HTTP headers in size.

The `response` option can be used to limit the [response data](#response-data):

```json
{
  "defaults": {
    "response": "tokens"
  }
}
```

This will return only the tokens, without the `raw` key.

In case you want to include the decoded `id_token` as well:

```json
{
  "google": {
    "response": ["tokens", "jwt"]
  }
}
```

This will make the decoded `id_token` available as `id_token_jwt` in the response object.

## Session

Grant uses session to persist state between HTTP redirects occurring during the OAuth flow. This session, however, was never meant to be used as persistent storage, even if that's totally possible.

Once you receive the [response data](#response-data) in your final `callback` route you are free to destroy that session, and do whatever you want with the returned data.

However, there are a few session keys returned in your final `callback` route, that you may find useful:

Key        | Availability            | Description
:--        | :--                     | :--
`provider` | **Always**              | The provider [name](#grant) this authorization was called for
`override` | Depends on URL          | The [static override](#static-overrides) name used for this authorization
`dynamic`  | Depends on request type | The [dynamic override](#dynamic-override) configuration passed for this authorization
`state`    | OAuth 2.0 only          | OAuth 2.0 state string that was generated
`nonce`    | OpenID Connect only     | [OpenID Connect](#openid-connect) nonce string that was generated
`code_verifier` | PKCE only     | The code verifier that was generated
`request`  | OAuth 1.0a only         | Data returned from the first request of the OAuth 1.0a flow
`response` | Depends on transport used | The final [response data](#response-data)

---

# Advanced


## All Available Options

Key | Location | Description
:---| :--- | :---
request_url | [oauth.json][oauth-config] | OAuth1/step1
authorize_url | [oauth.json][oauth-config] | OAuth1/step2 or OAuth2/step1
access_url | [oauth.json][oauth-config] | OAuth1/step3 or OAuth2/step2
oauth | [oauth.json][oauth-config] | OAuth version number
scope_delimiter | [oauth.json][oauth-config] | string delimiter used for concatenating multiple scopes
protocol, host, path | `defaults` | used to generate `redirect_uri`
transport | `defaults` | [transport](#response-data-transport) to use to deliver the response data in your final `callback` route
state | `defaults` | toggle random `state` string generation for OAuth2
key | `[provider]` | OAuth app key, reserved aliases: `consumer_key` and `client_id`
secret | `[provider]` | OAuth app secret, reserved aliases: `consumer_secret` and `client_secret`
scope | `[provider]` | list of scopes to request
custom_params | `[provider]` | custom authorization [parameters](#custom-parameters) and their values
subdomain | `[provider]` | string to be [embedded](#subdomain-urls) in `request_url`, `authorize_url` and `access_url`
nonce | `[provider]` | toggle random `nonce` string generation for [OpenID Connect](#openid-connect) providers
pkce | `[provider]` | toggle `pkce` support
callback | `[provider]` | final callback route on your server to receive the [response data](#response-data)
dynamic | `[provider]` | allow [dynamic override](#dynamic-override) of configuration
overrides | `[provider]` | [static overrides](#static-overrides) for a provider
response | `[provider]` | [limit](#limit-response-data) the response data
token_endpoint_auth_method | `[provider]` | authentication method for the [token endpoint](#token-endpoint-auth-method)
name | generated | provider's [name](#grant), used to generate `redirect_uri`
[provider] | generated | provider's [name](#grant) as key
redirect_uri | generated | OAuth app [redirect URI](#redirect-uri), generated using `protocol`, `host`, `path` and `name`


## Configuration Scopes

Grant relies on configuration gathered from **6** different places:

1. The **first** place Grant looks for configuration is the built-in [oauth.json][oauth-config] file located in the config folder.

2. The **second** place Grant looks for configuration is the `defaults` key, specified in the user's configuration. These defaults are applied for every provider in the user's configuration.

3. The **third** place for configuration is the provider itself. All providers in the user's configuration inherit every option defined for them in the [oauth.json][oauth-config] file, and all options defined inside the `defaults` key. Having [oauth.json][oauth-config] file and a `defaults` configuration is only a convenience. You can define all available options directly for a provider.

4. The **fourth** place for configuration are the provider's `overrides`. The [static overrides](#static-overrides) inherit their parent provider, essentially creating a sub provider of the same type.

5. The **fifth** place for configuration is the [dynamic state override](#dynamic-override). The request/response lifecycle state of your HTTP framework of choice can be used to dynamically override configuration.

6. The **sixth** place for configuration, that _[potentially](#oauth-proxy)_ can override all of the above, and make all of the above optional, is the [dynamic HTTP override](#dynamic-override).


## Custom Providers

You can define your own provider by adding a key for it in your configuration. In this case you'll have to supply all of the required options by yourself:

```json
{
  "defaults": {
    "protocol": "https",
    "host": "awesome.com"
  },
  "awesome": {
    "authorize_url": "https://awesome.com/authorize",
    "access_url": "https://awesome.com/token",
    "oauth": 2,
    "key": "APP_ID",
    "secret": "APP_SECRET",
    "scope": ["read", "write"]
  }
}
```

> Take a look at the [oauth.json][oauth-config] file to see how various providers are configured.


## Development Environments

You can easily configure different development environments:

```json
{
  "development": {
    "defaults": {"protocol": "http", "host": "localhost:3000"},
    "github": {
      "key": "development OAuth app credentials",
      "secret": "development OAuth app credentials"
    }
  },
  "staging": {
    "defaults": {"protocol": "https", "host": "staging.awesome.com"},
    "github": {
      "key": "staging OAuth app credentials",
      "secret": "staging OAuth app credentials"
    }
  },
  "production": {
    "defaults": {"protocol": "https", "host": "awesome.com"},
    "github": {
      "key": "production OAuth app credentials",
      "secret": "production OAuth app credentials"
    }
  }
}
```

Then you can pass the environment flag:

```bash
NODE_ENV=production node app.js
```

And use it in your application:

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

## OAuth Proxy

In case you really want to, you can allow [dynamic override](#dynamic-override) of every option for a provider:

```json
{
  "github": {
    "dynamic": true
  }
}
```

And the most extreme case is allowing even non preconfigured providers to be used dynamically:

```json
{
  "defaults": {
    "dynamic": true
  }
}
```

> Essentially Grant is a completely transparent **[OAuth Proxy][oauth-like-a-boss]**.


## Redirect URI

The `protocol`, the `host` (and optionally the `path`) options are used to generate **[the correct](#redirect-url)** `redirect_uri` for each provider:

```json
{
  "defaults": {
    "protocol": "https",
    "host": "awesome.com"
  },
  "google": {},
  "twitter": {}
}
```

The above configuration is identical to:

```json
{
  "google": {
    "redirect_uri": "https://awesome.com/connect/google/callback"
  },
  "twitter": {
    "redirect_uri": "https://awesome.com/connect/twitter/callback"
  }
}
```

> Note that the `redirect_uri` option would override the `protocol` and the `host` even if they were specified.


## Token Endpoint Auth Method

Grant is handling the OAuth 2.0 _token endpoint_ request internally.

By default all of the required OAuth parameters will be encoded in the request body, including the OAuth app credentials.

You can use the `token_endpoint_auth_method` option to send the OAuth app credentials as Basic authorization header instead:

```json
"google": {
  "token_endpoint_auth_method": "client_secret_basic"
}
```


## Meta Configuration

You can document your configuration by adding custom keys to it:

```json
{
  "google": {
    "meta": {
      "app": "My Awesome OAuth App",
      "owner": "my_email@gmail.com",
      "url": "https://url/to/manage/oauth/app"
    }
  }
}
```

> In the example above `meta` is an arbitrary key, but it cannot be one of the [reserved keys][reserved-keys].

---

# OAuth Quirks


## Subdomain URLs

Some providers have dynamic URLs containing bits of user information embedded in them.

The `subdomain` option can be used to specify your company name, server region or whatever else is required:

```json
"shopify": {
  "subdomain": "mycompany"
},
"battlenet": {
  "subdomain": "us"
}
```

Then Grant will generate the correct OAuth URLs:

```json
"shopify": {
  "authorize_url": "https://mycompany.myshopify.com/admin/oauth/authorize",
  "access_url": "https://mycompany.myshopify.com/admin/oauth/access_token"
},
"battlenet": {
  "authorize_url": "https://us.battle.net/oauth/authorize",
  "access_url": "https://us.battle.net/oauth/token"
}
```

> Alternatively you can override the entire `authorize_url` and `access_url` in your configuration.


## Sandbox OAuth URLs

Some providers may have *Sandbox* URLs to use while developing your app. To use them just override the entire `request_url`, `authorize_url` and `access_url` in your configuration *(notice the `sandbox` bits)*:

```json
"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"
}
```


## Sandbox Redirect URI

Very rarely you may need to override the `redirect_uri` that Grant [generates for you](#redirect-uri).

For example Feedly supports only `http://localhost` as redirect URL of their Sandbox OAuth application, and it won't allow the correct `http://localhost/connect/feedly/callback` URL:

```js
"feedly": {
  "redirect_uri": "http://localhost"
}
```

In this case 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('/', (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)}`)
  }
})
```

> As usual you will receive the [response data](#response-data) in your final `callback` route.


## Provider Quirks


> **Ebay**

Set the Redirect URL of your OAuth app as usual `[protocol]://[host]/connect/ebay/callback`. Then Ebay will generate a special string called *RuName (eBay Redirect URL name)* that you need to set as `redirect_uri` in Grant:

```json
"ebay": {
  "redirect_uri": "RUNAME"
}
```


> **Flickr, Freelancer, Optimizely**

Some providers are using custom authorization parameter to pass the requested scopes - Flickr `perms`, Freelancer `advanced_scopes`, Optimizely `scopes`, but you can use the regular `scope` option instead:

```json
"flickr": {
  "scope": ["write"]
},
"freelancer": {
  "scope": ["1", "2"]
},
"optimizely": {
  "scope": ["all"]
}
```


> **Mastodon**

Mastodon requires the entire *domain* of your server to be embedded in the OAuth URLs. However you should use the `subdomain` option:

```json
"mastodon": {
  "subdomain": "mastodon.cloud"
}
```


> **SurveyMonkey**

Set your Mashery user name as `key` and your application key as `api_key`:

```json
"surveymonkey": {
  "key": "MASHERY_USER_NAME",
  "secret": "CLIENT_SECRET",
  "custom_params": {"api_key": "CLIENT_ID"}
}
```


> **Fitbit, LinkedIn, ProjectPlace**

Initially these were OAuth1 providers, so the `linkedin` and `projectplace` names are used for that. To use their OAuth2 flow append `2` at the end of their names:

```js
"linkedin2": {
  // navigate to /connect/linkedin2
},
"projectplace2": {
  // navigate to /connect/projectplace2
}
```


> **VisualStudio**

Set your Client Secret as `secret` not the App Secret:

```json
"visualstudio": {
  "key": "APP_ID",
  "secret": "CLIENT_SECRET instead of APP_SECRET"
}
```

---

# Misc


## Alternative Require

Alternatively you can require any of the middlewares directly from `grant` *(each pair is identical)*:

```js
// Express
var Grant = require('grant-express')
var Grant = require('grant').express()
// Koa
var Grant = require('grant-koa')
var Grant = require('grant').koa()
// Hapi
var Grant = require('grant-hapi')
var Grant = require('grant').hapi()
```


## Alternative Instantiation

Grant can be instantiated with or without using the `new` keyword:

```js
var Grant = require('grant-express|koa|hapi')
var grant = Grant(config)
// identical to:
var grant = new Grant(config)
```

Additionally Hapi accepts the configuration in two different ways:

```js
server.register([{plugin: grant(config)}])
// identical to:
server.register([{plugin: grant(), options: config}])
```


## Programmatic Access

Every Grant instance have a `config` property attached to it:

```js
var grant = Grant(require('./config'))
console.log(grant.config)
```

It contains the _generated_ configuration that Grant uses internally.

You can use the `config` property to alter the Grant's behavior during runtime. Keep in mind that this affects the **entire** Grant instance! Use [dynamic override](#dynamic-override) instead, to alter configuration per authorization attempt.


## Get User Profile

Once you have your access tokens secured, you can start making authorized requests on behalf of your users.

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

```js
var express = require('express')
var session = require('express-session')
var grant = require('grant-express')
var request = require('request-compose').client

var config = {
  "defaults": {
    "protocol": "http",
    "host": "localhost:3000"
  },
  "facebook": {
    "key": "APP_ID",
    "secret": "APP_SECRET",
    "callback": "/hello"
  }
}

express()
  .use(session({secret: 'grant', saveUninitialized: true, resave: true}))
  .use(grant(config))
  .get('/hello', async (req, res) => {
    var {body} = await request({
      url: 'https://graph.facebook.com/me',
      headers: {authorization: `Bearer ${req.query.access_token}`}
    })
    res.end(JSON.stringify({oauth: req.query, profile: body}, null, 2))
  })
  .listen(3000)
```


  [npm-version]: https://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)

  [npm]: https://www.npmjs.com/package/grant
  [travis]: https://travis-ci.org/simov/grant
  [coveralls]: https://coveralls.io/r/simov/grant?branch=master

  [grant-oauth]: https://grant.outofindex.com
  [oauth-like-a-boss]: https://dev.to/simov/oauth-like-a-boss-2m3b

  [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/examples#table-of-contents
  [changelog]: https://github.com/simov/grant/blob/master/CHANGELOG.md

  [session-transport-example]: https://github.com/simov/grant/blob/master/examples/session-transport
  [oauth-proxy-example]: https://github.com/simov/grant/blob/master/examples/oauth-proxy
  [openid-connect-example]: https://github.com/simov/grant/blob/master/examples/openid-connect
