README


Grant

[![npm-version]][npm] [![test-ci-img]][test-ci-url] [![test-cov-img]][test-cov-url] [![snyk-vulnerabilities]][snyk]

_OAuth Proxy_


200+ 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) | [adobe](https://www.adobe.io) | [aha](https://www.aha.io/api) | [alchemer](https://apihelp.alchemer.com) | [amazon](https://login.amazon.com/documentation) | [angellist](https://angel.co/api) | [apple](https://developer.apple.com) | [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) | [authing](https://www.authing.cn/developer) | [autodesk](https://forge.autodesk.com) | [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) | [crossid](https://developer.crossid.io) | [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://discord.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) | [figma](https://www.figma.com/developers) | [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/) | [garmin](https://developer.garmin.com) | [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://docs.github.com/developers) | [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) | [huddle](https://www.huddle.com/huddle-api/) | [ibm](https://ibm.biz/provisioner) | [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) | [keycloak](https://www.keycloak.org) | [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/) | [mendeley](https://dev.mendeley.com) | [mention](https://dev.mention.com) | [microsoft](https://developer.microsoft.com/en-us/graph) | [mixcloud](https://www.mixcloud.com/developers) | [moxtra](https://developer.moxtra.com) | [myob](https://developer.myob.com) | [naver](https://developers.naver.com) | [nest](https://developers.nest.com) | [netlify](https://docs.netlify.com) | [nokotime](https://developer.nokotime.com) | [notion](https://developers.notion.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) | [procore](https://developers.procore.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) | [sellsy](https://api.sellsy.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) | [snowflake](https://docs.snowflake.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) | [storyblok](https://www.storyblok.com/docs/guide/introduction) | [strava](https://developers.strava.com) | [stripe](https://stripe.com/docs) | [surveymonkey](https://developer.surveymonkey.com) | [surveysparrow](https://surveysparrow.com/developer) | [thingiverse](https://www.thingiverse.com/developers) | [ticketbud](https://api.ticketbud.com) | [tiktok](https://developers.tiktok.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) | [trustpilot](https://developers.trustpilot.com) | [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) | [unbounce](https://developer.unbounce.com) | [underarmour](https://developer.underarmour.com) | [unsplash](https://unsplash.com/documentation) | [untappd](https://untappd.com/api/docs) | [upwork](https://developers.upwork.com) | [uservoice](https://developer.uservoice.com) | [vend](https://developers.vendhq.com) | [venmo](https://developers.braintreepayments.com/guides/venmo/overview/) | [vercel](https://vercel.com/docs) | [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) | [zendesk](https://developer.zendesk.com) | [zoom](https://marketplace.zoom.us/docs)

Table of Contents


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


- Handlers
  - Express / Koa / Hapi / Fastify
- Configuration
- Connect
- Callback
- Dynamic Configuration
- Misc
- Examples
  - [express][examples] / [koa][examples] / [hapi][examples] / [fastify][examples] / [aws][grant-aws] / [azure][grant-azure] / [gcloud][grant-gcloud] / [vercel][grant-vercel]
- [Changelog][changelog]


Handlers


HTTP Frameworks


Express

  1. ``` js
  2. var express = require('express')
  3. var session = require('express-session')
  4. var grant = require('grant').express()

  5. var app = express()
  6. // REQUIRED: any session store - see /examples/handler-express
  7. app.use(session({secret: 'grant'}))
  8. // mount grant
  9. app.use(grant({/*configuration - see below*/}))
  10. ```
Koa

  1. ``` js
  2. var Koa = require('koa')
  3. var session = require('koa-session')
  4. var grant = require('grant').koa()

  5. var app = new Koa()
  6. // REQUIRED: any session store - see /examples/handler-koa
  7. app.keys = ['grant']
  8. app.use(session(app))
  9. // mount grant
  10. app.use(grant({/*configuration - see below*/}))
  11. ```
Hapi

  1. ``` js
  2. var Hapi = require('hapi')
  3. var yar = require('yar')
  4. var grant = require('grant').hapi()

  5. var server = new Hapi.Server()
  6. server.register([
  7.   // REQUIRED: any session store - see /examples/handler-hapi
  8.   {plugin: yar, options: {cookieOptions: {password: 'grant', isSecure: false}}},
  9.   // mount grant
  10.   {plugin: grant({/*configuration - see below*/})}
  11. ])
  12. ```
Fastify

  1. ``` js
  2. var fastify = require('fastify')
  3. var cookie = require('fastify-cookie')
  4. var session = require('@fastify/session')
  5. var grant = require('grant').fastify()

  6. fastify()
  7.   .register(cookie)
  8.   .register(session, {secret: 'grant', cookie: {secure: false}})
  9.   .register(grant({/*configuration - see below*/}))
  10. ```

Serverless Functions


AWS Lambda

  1. ``` js
  2. var grant = require('grant').aws({
  3.   config: {/*configuration - see below*/}, session: {secret: 'grant'}
  4. })

  5. exports.handler = async (event) => {
  6.   var {redirect, response} = await grant(event)
  7.   return redirect || {
  8.     statusCode: 200,
  9.     headers: {'content-type': 'application/json'},
  10.     body: JSON.stringify(response)
  11.   }
  12. }
  13. ```
Azure Function

  1. ``` js
  2. var grant = require('grant').azure({
  3.   config: {/*configuration - see below*/}, session: {secret: 'grant'}
  4. })

  5. module.exports = async (context, req) => {
  6.   var {redirect, response} = await grant(req)
  7.   return redirect || {
  8.     status: 200,
  9.     headers: {'content-type': 'application/json'},
  10.     body: JSON.stringify(response)
  11.   }
  12. }
  13. ```
Google Cloud Function

  1. ``` js
  2. var grant = require('grant').gcloud({
  3.   config: {/*configuration - see below*/}, session: {secret: 'grant'}
  4. })

  5. exports.handler = async (req, res) => {
  6.   var {response} = await grant(req, res)
  7.   if (response) {
  8.     res.statusCode = 200
  9.     res.setHeader('content-type', 'application/json')
  10.     res.end(JSON.stringify(response))
  11.   }
  12. }
  13. ```
Vercel

  1. ``` js
  2. var grant = require('grant').vercel({
  3.   config: {/*configuration - see below*/}, session: {secret: 'grant'}
  4. })

  5. module.exports = async (req, res) => {
  6.   var {response} = await grant(req, res)
  7.   if (response) {
  8.     res.statusCode = 200
  9.     res.setHeader('content-type', 'application/json')
  10.     res.end(JSON.stringify(response))
  11.   }
  12. }
  13. ```

Examples


__[express][examples] / [koa][examples] / [hapi][examples] / [fastify][examples] / [aws][grant-aws] / [azure][grant-azure] / [gcloud][grant-gcloud] / [vercel][grant-vercel]__




Configuration


Configuration: Basics


  1. ``` json
  2. {
  3.   "defaults": {
  4.     "origin": "http://localhost:3000",
  5.     "transport": "session",
  6.     "state": true
  7.   },
  8.   "google": {
  9.     "key": "...",
  10.     "secret": "...",
  11.     "scope": ["openid"],
  12.     "nonce": true,
  13.     "custom_params": {"access_type": "offline"},
  14.     "callback": "/hello"
  15.   },
  16.   "twitter": {
  17.     "key": "...",
  18.     "secret": "...",
  19.     "callback": "/hi"
  20.   }
  21. }
  22. ```

- defaults - default configuration for all providers
  - origin - where your client server can be reached http://localhost:3000 | https://site.com ...
  - transport - a transport used to deliver the response data in yourcallback route
  - state - generate random state string
- provider - any supported providergoogle | 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 (OpenID Connect only)
  - custom_params - custom authorization 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 client server can be reached
prefix | defaults | Path prefix for the Grant internal routes
state | defaults | Random state string for OAuth 2.0
nonce | defaults | Random nonce string for OpenID Connect
pkce | defaults | Toggle 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
[provider] | generated | Provider's name 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/hi'
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 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_ can override all of the above, and make all of the above optional, is the [dynamic](#dynamic-http) HTTP override.


Connect


Connect: Origin


The origin is where your client server can be reached:

  1. ``` json
  2. {
  3.   "defaults": {
  4.     "origin": "http://localhost:3000"
  5.   }
  6. }
  7. ```

You login by navigating to the /connect/:provider route where :provider is a key in your configuration, usually one of the officially supported ones, but you can define your own as well. Additionally you can login through a static override defined for that provider by navigating to the/connect/:provider/:override? route.

Connect: Prefix


By default Grant operates on the following two routes:

  1. ```
  2. /connect/:provider/:override?
  3. /connect/:provider/callback
  4. ```

However, the default /connect prefix can be configured:

  1. ``` json
  2. {
  3.   "defaults": {
  4.     "origin": "http://localhost:3000",
  5.     "prefix": "/oauth"
  6.   }
  7. }
  8. ```

Connect: Redirect URI


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

  1. ```
  2. [origin][prefix]/[provider]/callback
  3. ```

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 key:

  1. ``` json
  2. {
  3.   "google": {
  4.     "custom_params": {"access_type": "offline", "prompt": "consent"}
  5.   },
  6.   "reddit": {
  7.     "custom_params": {"duration": "permanent"}
  8.   },
  9.   "trello": {
  10.     "custom_params": {"name": "my app", "expiration": "never"}
  11.   }
  12. }
  13. ```

Connect: OpenID Connect


The openid scope is required, and generating a random nonce string is optional but recommended:

  1. ``` json
  2. {
  3.   "google": {
  4.     "scope": ["openid"],
  5.     "nonce": true
  6.   }
  7. }
  8. ```

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:

  1. ``` json
  2. {
  3.   "google": {
  4.     "pkce": true
  5.   }
  6. }
  7. ```

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:

  1. ``` json
  2. {
  3.   "github": {
  4.     "key": "...", "secret": "...",
  5.     "scope": ["public_repo"],
  6.     "callback": "/hello",
  7.     "overrides": {
  8.       "notifications": {
  9.         "key": "...", "secret": "...",
  10.         "scope": ["notifications"]
  11.       },
  12.       "all": {
  13.         "scope": ["repo", "gist", "user"],
  14.         "callback": "/hey"
  15.       }
  16.     }
  17.   }
  18. }
  19. ```

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 scopes and also receive the response data in another callback route


Callback


Callback: Data


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

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

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

OAuth 2.0


  1. ``` js
  2. {
  3.   id_token: '...',
  4.   access_token: '...',
  5.   refresh_token: '...',
  6.   raw: {
  7.     id_token: '...',
  8.     access_token: '...',
  9.     refresh_token: '...',
  10.     some: 'other data'
  11.   }
  12. }
  13. ```

The refresh_token is optional. The id_token is returned only for OpenID Connect providers requesting theopenid scope.

OAuth 1.0a


  1. ``` js
  2. {
  3.   access_token: '...',
  4.   access_secret: '...',
  5.   raw: {
  6.     oauth_token: '...',
  7.     oauth_token_secret: '...',
  8.     some: 'other data'
  9.   }
  10. }
  11. ```

Error


  1. ``` js
  2. {
  3.   error: {
  4.     some: 'error data'
  5.   }
  6. }
  7. ```

Callback: Transport


querystring


By default Grant will encode the OAuth response data asquerystring in your callback route or absolute URL:

  1. ``` json
  2. {
  3.   "github": {
  4.     "callback": "https://site.com/hello"
  5.   }
  6. }
  7. ```

This is useful when using Grant as OAuth Proxy. However this finalhttps://site.com/hello?access_token=... redirect may potentially leak private data in your server logs, especially when sitting behind a reverse proxy.

session


For local callback routes the session transport is recommended:

  1. ``` json
  2. {
  3.   "defaults": {
  4.     "transport": "session"
  5.   },
  6.   "github": {
  7.     "callback": "/hello"
  8.   }
  9. }
  10. ```

This will make the OAuth response data available in thesession object instead:

  1. ``` js
  2. req.session.grant.response // Express
  3. ctx.session.grant.response // Koa
  4. req.yar.get('grant').response // Hapi
  5. req.session.grant.response // Fastify
  6. (await session.get()).grant.response // Serverless Function
  7. ```

state


The request/response lifecycle state can be used as well:

  1. ``` json
  2. {
  3.   "defaults": {
  4.     "transport": "state"
  5.   }
  6. }
  7. ```

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

  1. ``` js
  2. res.locals.grant.response // Express
  3. ctx.state.grant.response // Koa
  4. req.plugins.grant.response // Hapi
  5. res.grant.response // Fastify
  6. var {response} = await grant(...) // Serverless Function
  7. ```

Callback: Response


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

  1. ``` js
  2. {
  3.   id_token: '...',
  4.   access_token: '...',
  5.   refresh_token: '...',
  6.   raw: {
  7.     id_token: '...',
  8.     access_token: '...',
  9.     refresh_token: '...',
  10.     some: 'other data'
  11.   }
  12. }
  13. ```

querystring


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

  1. ``` json
  2. {
  3.   "defaults": {
  4.     "response": ["tokens"]
  5.   }
  6. }
  7. ```

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

This is useful when using Grant as 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.

  1. ``` json
  2. {
  3.   "google": {
  4.     "response": ["tokens"]
  5.   }
  6. }
  7. ```

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

jwt


Grant can also return even larger response data by including the decoded JWT for OpenID Connect providers that returnid_token:

  1. ``` json
  2. {
  3.   "google": {
  4.     "response": ["tokens", "raw", "jwt"]
  5.   }
  6. }
  7. ```

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

  1. ``` js
  2. {
  3.   id_token: '...',
  4.   access_token: '...',
  5.   refresh_token: '...',
  6.   raw: {
  7.     id_token: '...',
  8.     access_token: '...',
  9.     refresh_token: '...',
  10.     some: 'other data'
  11.   },
  12.   jwt: {id_token: {header: {}, payload: {}, signature: '...'}}
  13. }
  14. ```

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

profile


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

  1. ``` json
  2. {
  3.   "google": {
  4.     "response": ["tokens", "profile"]
  5.   }
  6. }
  7. ```

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

  1. ``` js
  2. {
  3.   access_token: '...',
  4.   refresh_token: '...',
  5.   profile: {some: 'user data'}
  6. }
  7. ```

The profile key contains either the raw response data returned by 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 accessible only when certain scopes were requested.

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 though that's totally possible.

Once you receive the response data in yourcallback 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 used for this authorization
override | Depends on URL          | The static override name used for this authorization
dynamic  | Depends on request type | The dynamic override configuration passed to this authorization
state    | OAuth 2.0 only          | OAuth 2.0 state string that was generated
nonce    | OpenID Connect only     | OpenID Connect nonce string that was generated
code_verifier | PKCE only     | The code verifier that was generated for 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


Dynamic Configuration


Dynamic: Instance


Every Grant instance have a config property attached to it:

  1. ``` js
  2. var grant = Grant(require('./config'))
  3. console.log(grant.config)
  4. ```

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

This property contains the generated configuration used internally by Grant, and changes made to that configuration affects the entire Grant instance!

Dynamic: State


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

  1. ``` js
  2. var state = {dynamic: {subdomain: 'usershop'}}
  3. res.locals.grant = state // Express
  4. ctx.state.grant = state // Koa
  5. req.plugins.grant = state // Hapi
  6. req.grant = state // Fastify
  7. await grant(..., state) // Serverless Function
  8. ```

This is useful in cases when you want to configure Grant dynamically with potentially sensitive data that you don't want to send over HTTP.

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 using a state override.

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:

  1. ``` json
  2. {
  3.   "shopify": {
  4.     "dynamic": ["subdomain"]
  5.   }
  6. }
  7. ```

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

  1. ``` html
  2. <form action="/connect/shopify" method="POST" accept-charset="utf-8">
  3.   <input type="text" name="subdomain" value="" />
  4.   <button>Login</button>
  5. </form>
  6. ```

Making POST request to the /connect/:provider/:override? route requires a form body parser middleware:

  1. ``` js
  2. .use(require('body-parser').urlencoded({extended: true})) // Express
  3. .use(require('koa-bodyparser')()) // Koa
  4. .register(require('fastify-formbody')) // Fastify
  5. ```

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

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

Any dynamic configuration sent over HTTP GET/POST request overrides 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:

  1. ``` json
  2. {
  3.   "github": {
  4.     "dynamic": true
  5.   }
  6. }
  7. ```

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

  1. ``` json
  2. {
  3.   "defaults": {
  4.     "dynamic": true
  5.   }
  6. }
  7. ```

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:

  1. ``` json
  2. {
  3.   "defaults": {
  4.     "origin": "https://mysite.com"
  5.   },
  6.   "google": {},
  7.   "twitter": {}
  8. }
  9. ```

The above configuration is identical to:

  1. ``` json
  2. {
  3.   "google": {
  4.     "redirect_uri": "https://mysite.com/connect/google/callback"
  5.   },
  6.   "twitter": {
  7.     "redirect_uri": "https://mysite.com/connect/twitter/callback"
  8.   }
  9. }
  10. ```

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:

  1. ``` json
  2. {
  3.   "defaults": {
  4.     "origin": "http://localhost:3000"
  5.   },
  6.   "awesome": {
  7.     "authorize_url": "https://awesome.com/authorize",
  8.     "access_url": "https://awesome.com/token",
  9.     "oauth": 2,
  10.     "key": "...",
  11.     "secret": "...",
  12.     "scope": ["read", "write"]
  13.   }
  14. }
  15. ```

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:

  1. ``` json
  2. {
  3.   "google": {
  4.     "meta": {
  5.       "app": "My Awesome OAuth App",
  6.       "owner": "my_email@gmail.com",
  7.       "url": "https://url/to/manage/oauth/app"
  8.     }
  9.   }
  10. }
  11. ```

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

Misc: Handler Constructors


Grant supports different ways of instantiation:

  1. ``` js
  2. // Express or any other handler
  3. var grant = require('grant').express()(config)
  4. var grant = require('grant').express()({config, ...})
  5. var grant = require('grant').express(config)
  6. var grant = require('grant').express({config, ...})
  7. var grant = require('grant')({handler: 'express', config, ...})
  8. ```

Using the new keyword is optional:

  1. ``` js
  2. var Grant = require('grant').express()
  3. var grant = Grant(config)
  4. var grant = new Grant(config)
  5. ```

Additionally Hapi accepts the configuration in two different ways:

  1. ``` js
  2. server.register([{plugin: grant(config)}])
  3. server.register([{plugin: grant(), options: config}])
  4. ```

Misc: Path Prefix


You can mount Grant under specific path prefix:

  1. ``` js
  2. // Express
  3. app.use('/oauth', grant(config))
  4. // Koa - using koa-mount
  5. app.use(mount('/oauth', grant(config)))
  6. // Hapi
  7. server.register([{routes: {prefix: '/oauth'}, plugin: grant(config)}])
  8. // Fastify
  9. server.register(grant(config), {prefix: '/oauth'})
  10. ```

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

  1. ``` json
  2. {
  3.   "defaults": {
  4.     "origin": "http://localhost:3000",
  5.     "prefix": "/oauth/login"
  6.   }
  7. }
  8. ```

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:

  1. ``` json
  2. {
  3.   "github": {
  4.     "callback": "/oauth/login/hello"
  5.   }
  6. }
  7. ```

Misc: Request


The underlying [HTTP client] can be configured using the request option:

  1. ``` js
  2. var grant = require('grant').express({
  3.   config,
  4.   request: {agent, timeout: 5000}
  5. })
  6. ```

Fancy [request logs] are available too:

  1. ``` sh
  2. npm i --save-dev request-logs
  3. DEBUG=req,res,json node app.js
  4. ```

Misc: ES Modules and TypeScript


Import Grant in your .mjs files:

  1. ``` js
  2. import express from 'express'
  3. import session from 'express-session'
  4. import grant from 'grant'
  5. import config from './config.json'

  6. express()
  7.   .use(session({}))
  8.   .use(grant.express(config))
  9. ```

Importing a .json file may require additional flag:

  1. ``` sh
  2. node --experimental-json-modules app.mjs
  3. ```

Grant ships with extensive [type definitions][type-definitions] for TypeScript. Additonal type definitions and examples can be found [here][grant-types].

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:

  1. ``` json
  2. "shopify": {
  3.   "subdomain": "mycompany"
  4. },
  5. "battlenet": {
  6.   "subdomain": "us"
  7. }
  8. ```

Then Grant will generate the correct OAuth URLs:

  1. ``` json
  2. "shopify": {
  3.   "authorize_url": "https://mycompany.myshopify.com/admin/oauth/authorize",
  4.   "access_url": "https://mycompany.myshopify.com/admin/oauth/access_token"
  5. },
  6. "battlenet": {
  7.   "authorize_url": "https://us.battle.net/oauth/authorize",
  8.   "access_url": "https://us.battle.net/oauth/token"
  9. }
  10. ```

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

  1. ``` json
  2. "paypal": {
  3.   "authorize_url": "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
  4.   "access_url": "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice"
  5. },
  6. "evernote": {
  7.   "request_url": "https://sandbox.evernote.com/oauth",
  8.   "authorize_url": "https://sandbox.evernote.com/OAuth.action",
  9.   "access_url": "https://sandbox.evernote.com/oauth"
  10. }
  11. ```

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:

  1. ``` json
  2. "feedly": {
  3.   "redirect_uri": "http://localhost"
  4. }
  5. ```

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:

  1. ``` js
  2. var qs = require('querystring')

  3. app.get('/', (req, res) => {
  4.   if (process.env.NODE_ENV === 'development' &&
  5.       req.session.grant &&
  6.       req.session.grant.provider === 'feedly' &&
  7.       req.query.code
  8.   ) {
  9.     res.redirect(`/connect/${req.session.grant.provider}/callback?${qs.stringify(req.query)}`)
  10.   }
  11. })
  12. ```

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:

  1. ``` json
  2. "ebay": {
  3.   "redirect_uri": "RUNAME"
  4. }
  5. ```

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:

  1. ``` json
  2. "flickr": {
  3.   "scope": ["write"]
  4. },
  5. "freelancer": {
  6.   "scope": ["1", "2"]
  7. },
  8. "optimizely": {
  9.   "scope": ["all"]
  10. }
  11. ```

Mastodon


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

  1. ``` json
  2. "mastodon": {
  3.   "subdomain": "mastodon.cloud"
  4. }
  5. ```

SurveyMonkey


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

  1. ``` json
  2. "surveymonkey": {
  3.   "key": "MASHERY_USER_NAME",
  4.   "secret": "CLIENT_SECRET",
  5.   "custom_params": {"api_key": "CLIENT_ID"}
  6. }
  7. ```

Twitter


Twitter OAuth 1.0a custom scope parameter can be specified in two ways:

  1. ``` json
  2. "twitter": {
  3.   "custom_params": {"x_auth_access_type": "read"}
  4. }
  5. "twitter": {
  6.   "scope": ["read"]
  7. }
  8. ```

Twitter OAuth 2.0 applications have to use the twitter2 provider:

  1. ``` json
  2. "twitter2": {
  3.   "state": true,
  4.   "pkce": true,
  5.   "scope": [
  6.     "users.read",
  7.     "tweet.read"
  8.   ]
  9. }
  10. ```

VisualStudio


Set your Client Secret as secret not the App Secret:

  1. ``` json
  2. "visualstudio": {
  3.   "key": "APP_ID",
  4.   "secret": "CLIENT_SECRET instead of APP_SECRET"
  5. }
  6. ```


  [npm-version]: https://img.shields.io/npm/v/grant.svg?style=flat-square (NPM Version)
  [test-ci-img]: https://img.shields.io/travis/simov/grant/master.svg?style=flat-square (Build Status)
  [test-cov-img]: https://img.shields.io/coveralls/simov/grant.svg?style=flat-square (Test Coverage)
  [snyk-vulnerabilities]: https://img.shields.io/snyk/vulnerabilities/npm/grant.svg?style=flat-square (Vulnerabilities)

  [npm]: https://www.npmjs.com/package/grant
  [test-ci-url]: https://github.com/simov/grant/actions/workflows/test.yml
  [test-cov-url]: https://coveralls.io/r/simov/grant?branch=master
  [snyk]: https://snyk.io/test/npm/grant

  [grant-oauth]: https://grant.outofindex.com
  [oauth-like-a-boss]: https://dev.to/simov/oauth-like-a-boss-2m3b
  [http client]: https://github.com/simov/request-compose
  [request logs]: https://github.com/simov/request-logs

  [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

  [grant-aws]: https://github.com/simov/grant-aws
  [grant-azure]: https://github.com/simov/grant-azure
  [grant-gcloud]: https://github.com/simov/grant-gcloud
  [grant-vercel]: https://github.com/simov/grant-vercel

  [grant-types]: https://github.com/simov/grant-types
  [type-definitions]: https://github.com/simov/grant/blob/master/grant.d.ts