npm package: @instadukan/grant
Description: OAuth Proxy
Author: Simeon Velichkov
Latest version: 5.1.1


# Grant

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

> _OAuth Proxy_

## 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) | [`cas`](https://apereo.github.io/cas/) | [`cheddar`](https://cheddarapp.com/developer) | [`clio`](https://app.clio.com/api/v4/documentation) | [`cognito`](https://aws.amazon.com/cognito/) | [`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) | [`fusionauth`](https://fusionauth.io/docs/) | [`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) | [`logingov`](https://developers.login.gov) | [`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

### [Migration Guide: from v4 to v5][migration]

- **[Providers](#grant)**
- **Handlers**
  - [Express](#express) / [Koa](#koa) / [Hapi](#hapi)
- **Configuration**
  - [Basics](#configuration-basics) / [Description](#configuration-description) / [Values](#configuration-values) / [Scopes](#configuration-scopes)
- **Connect**
  - [Origin](#connect-origin) / [Prefix](#connect-prefix) / [Redirect URI](#connect-redirect-uri) / [Custom Parameters](#connect-custom-parameters) / [OpenID Connect](#connect-openid-connect) / [PKCE](#connect-pkce) / [Static Overrides](#connect-static-overrides)
- **Callback**
  - [Data](#callback-data) / [Transport](#callback-transport) / [Response](#callback-response) / [Session](#callback-session)
- **Dynamic Configuration**
  - [Instance](#dynamic-instance) / [State](#dynamic-state) / [HTTP](#dynamic-http) / [OAuth Proxy](#dynamic-oauth-proxy)
- **Misc**
  - [Configuration](#misc-redirect-uri) / [Middlewares](#misc-alternative-require) / [OAuth Quirks](#misc-oauth-quirks)
- **[Examples][examples]**
- **[Changelog][changelog]**

----

# Handlers


## 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/handler-express
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/handler-koa
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/handler-hapi
  {plugin: yar, options: {cookieOptions: {password: 'grant', isSecure: false}}},
  // mount grant
  {plugin: grant(), options: {/*configuration - see below*/}}
])
```

---

# Configuration


## Configuration: Basics

```json
{
  "defaults": {
    "origin": "http://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
  - **origin** - where your client server can be reached `http://localhost:3000` | `https://site.com` ...
  - **transport** - a [transport](#callback-transport) to use to deliver the [response data](#callback-response) in your `callback` route
  - **state** - generate random state string on each authorization attempt
- **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 ([OpenID Connect](#connect-openid-connect) only)
  - **custom_params** - custom [authorization parameters](#connect-custom-parameters)
  - **callback** - relative route or absolute URL to receive the response data `/hello` | `https://site.com/hey` ...


## Configuration: Description

Key | Location | Description
:-| :-: | :-
***Authorization Server*** |
**`request_url`** | [oauth.json] | OAuth 1.0a only, first step
**`authorize_url`** | [oauth.json] | OAuth 2.0 first step, OAuth 1.0a second step
**`access_url`** | [oauth.json] | OAuth 2.0 second step, OAuth 1.0a third step
**`oauth`** | [oauth.json] | OAuth version number
**`scope_delimiter`** | [oauth.json] | String delimiter used for concatenating multiple scopes
**`token_endpoint_auth_method`** | `[provider]` | Authentication method for the token endpoint
**`token_endpoint_auth_signing_alg`** | `[provider]` | Signing algorithm for the token endpoint
***Client Server*** |
**`origin`** | `defaults` | Where your server and Grant can be reached
**`prefix`** | `defaults` | Path prefix for the Grant internal routes
**`state`** | `defaults` | Random state string for OAuth2
**`nonce`** | `defaults` | Random nonce string for OpenID Connect
**`pkce`** | `defaults` | PKCE support
**`response`** | `defaults` | Response data to receive
**`transport`** | `defaults` | A way to deliver the response data
**`callback`** | `[provider]` | Relative or absolute URL to receive the response data
**`overrides`** | `[provider]` | Static configuration overrides for a provider
**`dynamic`** | `[provider]` | Configuration keys that can be overridden dynamically over HTTP
***Client App*** |
**`key`** **`client_id`** **`consumer_key`** | `[provider]` | The `client_id` or `consumer_key` of your OAuth app
**`secret`** **`client_secret`**  **`consumer_secret`** | `[provider]` | The `client_secret` or `consumer_secret` of your OAuth app
**`scope`** | `[provider]` | List of scopes to request
**`custom_params`** | `[provider]` | Custom authorization parameters and their values
**`subdomain`** | `[provider]` | String to embed into the authorization server URLs
**`public_key`** | `[provider]` | Public PEM or JWK
**`private_key`** | `[provider]` | Private PEM or JWK
**`redirect_uri`** | `generated` | Absolute redirect URL of the OAuth app
***Grant*** |
**`name`** | `generated` | Provider's [name](#grant)
**`[provider]`** | `generated` | Provider's [name](#grant) as key
**`profile_url`** | [profile.json] | User profile URL


## Configuration: Values

Key | Location | Value
:- | :-: | :-:
***Authorization Server*** |
**`request_url`** | [oauth.json] | `'https://api.twitter.com/oauth/request_token'`
**`authorize_url`** | [oauth.json] | `'https://api.twitter.com/oauth/authenticate'`
**`access_url`** | [oauth.json] | `'https://api.twitter.com/oauth/access_token'`
**`oauth`** | [oauth.json] | `2` `1`
**`scope_delimiter`** | [oauth.json] | `','` `' '`
**`token_endpoint_auth_method`** | `[provider]` | `'client_secret_post'` `'client_secret_basic'` `'private_key_jwt'`
**`token_endpoint_auth_signing_alg`** | `[provider]` | `'RS256'` `'ES256'` `'PS256'`
***Client Server*** |
**`origin`** | `defaults` | `'http://localhost:3000'` `https://site.com`
**`prefix`** | `defaults` | `'/connect'` `/oauth` `''`
**`state`** | `defaults` | `true`
**`nonce`** | `defaults` | `true`
**`pkce`** | `defaults` | `true`
**`response`** | `defaults` | `['tokens', 'raw', 'jwt', 'profile']`
**`transport`** | `defaults` | `'querystring'` `'session'` `'state'`
**`callback`** | `[provider]` | `'/hello'` `'https://site.com/hey'`
**`overrides`** | `[provider]` | `{something: {scope: ['..']}}`
**`dynamic`** | `[provider]` | `['scope', 'subdomain']`
***Client App*** |
**`key`** **`client_id`** **`consumer_key`** | `[provider]` | `'123'`
**`secret`** **`client_secret`**  **`consumer_secret`** | `[provider]` | `'123'`
**`scope`** | `[provider]` | `['openid', '..']`
**`custom_params`** | `[provider]` | `{access_type: 'offline'}`
**`subdomain`** | `[provider]` | `'myorg'`
**`public_key`** | `[provider]` | `'..PEM..'` `'{..JWK..}'`
**`private_key`** | `[provider]` | `'..PEM..'` `'{..JWK..}'`
**`redirect_uri`** |`generated` | `'http://localhost:3000/connect/twitter/callback'`
***Grant*** |
**`name`** |`generated` | `name: 'twitter'`
**`[provider]`** |`generated` | `twitter: true`
**`profile_url`** | [profile.json] | `'https://api.twitter.com/1.1/users/show.json'`


## 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] 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] file, and all options defined inside the `defaults` key. Having [oauth.json] 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`](#connect-static-overrides). The static overrides inherit their parent provider, essentially creating new provider of the same type.

5. The **fifth** place for configuration is the dynamic [`state`](#dynamic-state) 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](#dynamic-oauth-proxy)_ can override all of the above, and make all of the above optional, is the [`dynamic`](#dynamic-http) HTTP override.

---

# Connect


## Connect: Origin

```json
{
  "defaults": {
    "origin": "http://localhost:3000"
  }
}
```

The `origin` is where your client server is listening to and can be reached.

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

## Connect: Prefix

By default Grant operates on the following two routes:

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

However, the default `/connect` prefix can be configured:

```json
{
  "defaults": {
    "origin": "http://localhost:3000",
    "prefix": "/oauth"
  }
}
```


## Connect: Redirect URI

The [`redirect_uri`](#misc-redirect-uri) of your OAuth app should follow this format:

```
[origin][prefix]/[provider]/callback
```

Where [`origin`](#connect-origin) and [`prefix`](#connect-prefix) have to match the ones set in your configuration, and [`provider`](#grant) is a provider key found in your configuration.

For example: `http://localhost:3000/connect/google/callback`

This redirect URI is used internally by Grant. Depending on the [`transport`](#callback-transport) being used you will receive the response data in the [`callback`](#callback-data) route or absolute URL configured for that provider.


## Connect: 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", "prompt": "consent"}
  },
  "reddit": {
    "custom_params": {"duration": "permanent"}
  },
  "trello": {
    "custom_params": {"name": "my app", "expiration": "never"}
  }
}
```


## Connect: OpenID Connect

The `openid` scope is required, and the `nonce` is optional but recommended:

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

Grant **does not** verify the signature of the returned `id_token` by default.

However, the following two claims of the `id_token` are being validated:

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


## Connect: PKCE

PKCE can be enabled for all providers or for a specific provider only:

```json
{
  "google": {
    "pkce": true
  }
}
```

Providers that do not support PKCE will ignore the additional parameters being sent.


## Connect: Static Overrides

Provider sub configurations can be configured 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": "/hey"
      }
    }
  }
}
```

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

---

# Callback


## Callback: Data

By default the response data is returned in your `callback` route or absolute URL encoded as querystring.

Depending on the [`transport`](#callback-transport) being used the response data can also be returned in the `session` or in the `state` object.

The amount of the returned data can also be controlled using the [`response`](#callback-response) option.

### OAuth 2.0

```js
{
  id_token: '...',
  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](#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'
  }
}
```


## Callback: Transport

### querystring

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

```json
{
  "github": {
    "callback": "https://site.com/hello"
  }
}
```

This is useful when using Grant as [OAuth Proxy](#dynamic-oauth-proxy). However this final `https://site.com/hello?access_token=...` redirect can potentially leak private data in your server logs, especially when sitting behind reverse proxy.

### session

For local `callback` routes the session `transport` is recommended:

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

This will make the OAuth [response data](#callback-data) available in the `session` object instead:

```js
req.session.grant.response // Express
ctx.session.grant.response // Koa
req.yar.get('grant').response // Hapi
```

### state

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

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

In this case `callback` route is not needed, and it will be ignored if provided. The response data will be available in the request/response lifecycle `state` instead:

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

## Callback: Response

By default Grant returns all of the available tokens and the `raw` response data returned from the Authorization server:

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

### querystring

When using the querystring [`transport`](#callback-transport) it might be a good idea to limit the response data:

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

This will return only the tokens available, without the `raw` response data.

This is useful when using Grant as [OAuth Proxy](#dynamic-oauth-proxy). 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.

### session

Using the session [`transport`](#callback-transport) is generally safer, but it also depends on the implementation of your session store.

In case your session store encodes the entire session in a cookie, not just the session ID, some servers may reject the HTTP request because of HTTP headers size being too big.

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

This will return only the tokens available, without the `raw` response data.

### jwt

Grant can also return even larger [response data](#callback-data) by including the decoded JWT for [OpenID Connect](#connect-openid-connect) providers that return `id_token`:

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

This will make the decoded JWT available in the response data:

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

Make sure you include all response keys that you want returned when configuring the `response` data explicitly.


### profile

Outside of the regular OAuth flow, Grant can request the user profile as well:

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

Additionaly a `profile` key will be available in the response data:

```js
{
  access_token: '...',
  refresh_token: '...',
  profile: {some: 'user data'}
}
```

The `profile` key contains either the raw response data returned from the user profile endpoint or an error message.

Not all of the supported providers have their `profile_url` set, and some of them might require custom parameters. Usually the user profile endpoint is only accessible if you request certain `scope`s.


## Callback: 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](#callback-data), in your `callback` route you are free to destroy that session.

However, there are a few session keys returned in your `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](#connect-static-overrides) name used for this authorization
`dynamic`  | Depends on request type | The [dynamic override](#dynamic-http) configuration passed for this authorization
`state`    | OAuth 2.0 only          | OAuth 2.0 state string that was generated
`nonce`    | OpenID Connect only     | [OpenID Connect](#connect-openid-connect) nonce string that was generated
`code_verifier` | PKCE only     | The code verifier that was generated for [PKCE](#connect-pkce)
`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](#callback-data)

---

# Dynamic Configuration


## Dynamic: Instance

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

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

You can use the `config` property to alter the Grant's behavior during runtime without having to restart your server.

Keep in mind that this property contains the **generated** configuration that Grant uses internally, and changes to that configuration affects the **entire** Grant instance!


## Dynamic: State

The request/response lifecycle state can be used to alter your configuration on every request:

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

Note that the request/response lifecycle `state` is not controlled by the [`dynamic`](#dynamic-http) configuration, meaning that you can override any configuration key.

Any allowed [`dynamic`](#dynamic-http) configuration key sent through HTTP GET/POST request will override the identical one set in `state`.

## Dynamic: HTTP

The `dynamic` configuration allows certain configuration keys to be set dynamically over HTTP GET/POST request.

For example `shopify` requires your shop name to be embedded into the OAuth URLs, so it makes sense to allow the [`subdomain`](#subdomain-urls) configuration key to be set dynamically:

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

Then you can have a web form in 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 make a `GET` request to the `/connect/:provider/:override?` route:

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

Note that `dynamic` configuration sent over HTTP GET/POST request override any other configuration.

## Dynamic: OAuth Proxy

In case you really want to, you can allow `dynamic` configuration override of every configuration key 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]**.

---

# Misc

## Misc: Redirect URI

The [`origin`](#connect-origin) and the [`prefix`](#connect-prefix) configuration is used to generate the correct [`redirect_uri`](#connect-redirect-uri) that Grant expects:

```json
{
  "defaults": {
    "origin": "https://mysite.com"
  },
  "google": {},
  "twitter": {}
}
```

The above configuration is identical to:

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

Note that explicitly specifying the `redirect_uri` overrides the one generated by default.


## Misc: Custom Providers

You can define your own provider by adding a key for it in your configuration. In this case all of the required configuration keys have to be specified:

```json
{
  "defaults": {
    "origin": "http://localhost:3000"
  },
  "awesome": {
    "authorize_url": "https://awesome.com/authorize",
    "access_url": "https://awesome.com/token",
    "oauth": 2,
    "key": "...",
    "secret": "...",
    "scope": ["read", "write"]
  }
}
```

Take a look at the [oauth.json] file on how various providers are being configured.


## Misc: 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"
    }
  }
}
```

Note that `meta` is an arbitrary key, but it cannot be one of the [reserved keys][reserved-keys].


## Misc: Alternative Require

All middlewares can be required 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()
```


## Misc: Alternative Instantiation

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

```js
var Grant = require('grant-express|koa|hapi')
// without using `new`
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}])
```

## Misc: Path Prefix

You can mount Grant under specific path prefix:

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

In this case the [`prefix`](#connect-prefix) configuration should reflect that + any other path parts that you may have:

```json
{
  "defaults": {
    "origin": "http://localhost:3000",
    "prefix": "/oauth/login"
  }
}
```

In this case you login by navigating to: `http://localhost:3000/oauth/login/:provider`

And the [`redirect_uri`](#connect-redirect-uri) of your OAuth app should be `http://localhost:3000/oauth/login/:provider/callback`

Optionally you can prefix your [`callback`](#callback) routes as well:

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

## Misc: OAuth Quirks

### Subdomain URLs

Some providers have dynamic URLs containing bits of user information embedded into them. Inside the main [oauth.json] configuration file such URLs contain a `[subdomain]` token embedded in them.

The `subdomain` option can be used to specify your company name, server region etc:

```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`](#connect-redirect-uri) that Grant generates for you.

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

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

In this case you'll have to redirect the user to the `[origin][prefix]/[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 in your final [`callback`](#callback) route.


### Provider Quirks


> **Ebay**

Set the Redirect URI of your OAuth app as usual `[origin][prefix]/[provider]/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"}
}
```


> **VisualStudio**

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

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

---


  [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.json]: https://github.com/simov/grant/blob/master/config/oauth.json
  [profile.json]: https://github.com/simov/grant/blob/master/config/profile.json
  [reserved-keys]: https://github.com/simov/grant/blob/master/config/reserved.json
  [examples]: https://github.com/simov/grant/tree/master/examples
  [changelog]: https://github.com/simov/grant/blob/master/CHANGELOG.md
  [migration]: https://github.com/simov/grant/blob/master/MIGRATION.md