grannt 5.4.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,1222 @@
1
+
2
+ # Grant
3
+
4
+ [![npm-version]][npm] [![test-ci-img]][test-ci-url] [![test-cov-img]][test-cov-url] [![snyk-vulnerabilities]][snyk]
5
+
6
+ > _OAuth Proxy_
7
+
8
+ ## 200+ Supported Providers / [OAuth Playground][grant-oauth]
9
+
10
+ [`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://developer.adobe.com) | [`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) | [`authentik`](https://docs.goauthentik.io/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) | [`flickr`](https://www.flickr.com/services) | [`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://developers.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) | [`osu`](https://osu.ppy.sh/docs) | [`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/准备工作_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`](https://developer.withings.com) | [`wordpress`](https://developer.wordpress.com) | [`workos`](https://workos.com/docs/sso) | [`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)
11
+
12
+
13
+ ## Table of Contents
14
+
15
+ ### [Migration Guide: from v4 to v5][migration]
16
+
17
+ - **[Providers](#grant)**
18
+ - **Handlers**
19
+ - [Express](#handlers) / [Koa](#handlers) / [Hapi](#handlers) / [Fastify](#handlers)
20
+ - [AWS Lambda](#handlers) / [Azure Function](#handlers) / [Google Cloud Function](#handlers) / [Vercel](#handlers)
21
+ - **Configuration**
22
+ - [Basics](#configuration-basics) / [Description](#configuration-description) / [Values](#configuration-values) / [Scopes](#configuration-scopes)
23
+ - **Connect**
24
+ - [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)
25
+ - **Callback**
26
+ - [Data](#callback-data) / [Transport](#callback-transport) / [Response](#callback-response) / [Session](#callback-session)
27
+ - **Dynamic Configuration**
28
+ - [Instance](#dynamic-instance) / [State](#dynamic-state) / [HTTP](#dynamic-http) / [OAuth Proxy](#dynamic-oauth-proxy)
29
+ - **Misc**
30
+ - [Configuration](#misc-redirect-uri) / [Handlers](#misc-handler-constructors) / [Request](#misc-request) / [Types](#misc-es-modules-and-typescript) / [OAuth Quirks](#misc-oauth-quirks)
31
+ - **Examples**
32
+ - [express][examples] / [koa][examples] / [hapi][examples] / [fastify][examples] / [aws][grant-aws] / [azure][grant-azure] / [gcloud][grant-gcloud] / [vercel][grant-vercel]
33
+ - **[Changelog][changelog]**
34
+
35
+ ----
36
+
37
+ # Handlers
38
+
39
+
40
+ ### HTTP Frameworks
41
+
42
+ <details><summary>Express</summary>
43
+
44
+ ```js
45
+ var express = require('express')
46
+ var session = require('express-session')
47
+ var grant = require('grant').express()
48
+
49
+ var app = express()
50
+ // REQUIRED: any session store - see /examples/handler-express
51
+ app.use(session({secret: 'grant'}))
52
+ // mount grant
53
+ app.use(grant({/*configuration - see below*/}))
54
+ ```
55
+ </details>
56
+
57
+ <details><summary>Koa</summary>
58
+
59
+ ```js
60
+ var Koa = require('koa')
61
+ var session = require('koa-session')
62
+ var grant = require('grant').koa()
63
+
64
+ var app = new Koa()
65
+ // REQUIRED: any session store - see /examples/handler-koa
66
+ app.keys = ['grant']
67
+ app.use(session(app))
68
+ // mount grant
69
+ app.use(grant({/*configuration - see below*/}))
70
+ ```
71
+ </details>
72
+
73
+ <details><summary>Hapi</summary>
74
+
75
+ ```js
76
+ var Hapi = require('hapi')
77
+ var yar = require('yar')
78
+ var grant = require('grant').hapi()
79
+
80
+ var server = new Hapi.Server()
81
+ server.register([
82
+ // REQUIRED: any session store - see /examples/handler-hapi
83
+ {plugin: yar, options: {cookieOptions: {password: 'grant', isSecure: false}}},
84
+ // mount grant
85
+ {plugin: grant({/*configuration - see below*/})}
86
+ ])
87
+ ```
88
+ </details>
89
+
90
+ <details><summary>Fastify</summary>
91
+
92
+ ```js
93
+ var fastify = require('fastify')
94
+ var cookie = require('@fastify/cookie')
95
+ var session = require('@fastify/session')
96
+ var grant = require('grant').fastify()
97
+
98
+ fastify()
99
+ .register(cookie)
100
+ .register(session, {secret: 'grant', cookie: {secure: false}})
101
+ .register(grant({/*configuration - see below*/}))
102
+ ```
103
+ </details>
104
+
105
+ ### Serverless Functions
106
+
107
+ <details><summary>AWS Lambda</summary>
108
+
109
+ ```js
110
+ var grant = require('grant').aws({
111
+ config: {/*configuration - see below*/}, session: {secret: 'grant'}
112
+ })
113
+
114
+ exports.handler = async (event) => {
115
+ var {redirect, response} = await grant(event)
116
+ return redirect || {
117
+ statusCode: 200,
118
+ headers: {'content-type': 'application/json'},
119
+ body: JSON.stringify(response)
120
+ }
121
+ }
122
+ ```
123
+ </details>
124
+
125
+ <details><summary>Azure Function</summary>
126
+
127
+ ```js
128
+ var grant = require('grant').azure({
129
+ config: {/*configuration - see below*/}, session: {secret: 'grant'}
130
+ })
131
+
132
+ module.exports = async (context, req) => {
133
+ var {redirect, response} = await grant(req)
134
+ return redirect || {
135
+ status: 200,
136
+ headers: {'content-type': 'application/json'},
137
+ body: JSON.stringify(response)
138
+ }
139
+ }
140
+ ```
141
+ </details>
142
+
143
+ <details><summary>Google Cloud Function</summary>
144
+
145
+ ```js
146
+ var grant = require('grant').gcloud({
147
+ config: {/*configuration - see below*/}, session: {secret: 'grant'}
148
+ })
149
+
150
+ exports.handler = async (req, res) => {
151
+ var {response} = await grant(req, res)
152
+ if (response) {
153
+ res.statusCode = 200
154
+ res.setHeader('content-type', 'application/json')
155
+ res.end(JSON.stringify(response))
156
+ }
157
+ }
158
+ ```
159
+ </details>
160
+
161
+ <details><summary>Vercel</summary>
162
+
163
+ ```js
164
+ var grant = require('grant').vercel({
165
+ config: {/*configuration - see below*/}, session: {secret: 'grant'}
166
+ })
167
+
168
+ module.exports = async (req, res) => {
169
+ var {response} = await grant(req, res)
170
+ if (response) {
171
+ res.statusCode = 200
172
+ res.setHeader('content-type', 'application/json')
173
+ res.end(JSON.stringify(response))
174
+ }
175
+ }
176
+ ```
177
+ </details>
178
+
179
+ ### Examples
180
+
181
+ > __[express][examples] / [koa][examples] / [hapi][examples] / [fastify][examples] / [aws][grant-aws] / [azure][grant-azure] / [gcloud][grant-gcloud] / [vercel][grant-vercel]__
182
+
183
+ > _[ES Modules and TypeScript](#misc-es-modules-and-typescript)_
184
+
185
+ ---
186
+
187
+ # Configuration
188
+
189
+
190
+ ## Configuration: Basics
191
+
192
+ ```json
193
+ {
194
+ "defaults": {
195
+ "origin": "http://localhost:3000",
196
+ "transport": "session",
197
+ "state": true
198
+ },
199
+ "google": {
200
+ "key": "...",
201
+ "secret": "...",
202
+ "scope": ["openid"],
203
+ "nonce": true,
204
+ "custom_params": {"access_type": "offline"},
205
+ "callback": "/hello"
206
+ },
207
+ "twitter": {
208
+ "key": "...",
209
+ "secret": "...",
210
+ "callback": "/hi"
211
+ }
212
+ }
213
+ ```
214
+
215
+ - **defaults** - default configuration for all providers
216
+ - **origin** - where your client server can be reached `http://localhost:3000` | `https://site.com` ...
217
+ - **transport** - a [transport](#callback-transport) used to deliver the [response data](#callback-response) in your `callback` route
218
+ - **state** - generate random state string
219
+ - **provider** - any [supported provider](#grant) `google` | `twitter` ...
220
+ - **key** - `consumer_key` or `client_id` of your OAuth app
221
+ - **secret** - `consumer_secret` or `client_secret` of your OAuth app
222
+ - **scope** - array of OAuth scopes to request
223
+ - **nonce** - generate random nonce string ([OpenID Connect](#connect-openid-connect) only)
224
+ - **custom_params** - custom [authorization parameters](#connect-custom-parameters)
225
+ - **callback** - relative route or absolute URL to receive the response data `/hello` | `https://site.com/hey` ...
226
+
227
+
228
+ ## Configuration: Description
229
+
230
+ Key | Location | Description
231
+ :-| :-: | :-
232
+ ***Authorization Server*** |
233
+ **`request_url`** | [oauth.json] | OAuth 1.0a only, first step
234
+ **`authorize_url`** | [oauth.json] | OAuth 2.0 first step, OAuth 1.0a second step
235
+ **`access_url`** | [oauth.json] | OAuth 2.0 second step, OAuth 1.0a third step
236
+ **`oauth`** | [oauth.json] | OAuth version number
237
+ **`scope_delimiter`** | [oauth.json] | String delimiter used for concatenating multiple scopes
238
+ **`token_endpoint_auth_method`** | `[provider]` | Authentication method for the token endpoint
239
+ **`token_endpoint_auth_signing_alg`** | `[provider]` | Signing algorithm for the token endpoint
240
+ ***Client Server*** |
241
+ **`origin`** | `defaults` | Where your client server can be reached
242
+ **`prefix`** | `defaults` | Path prefix for the Grant internal routes
243
+ **`state`** | `defaults` | Random state string for OAuth 2.0
244
+ **`nonce`** | `defaults` | Random nonce string for OpenID Connect
245
+ **`pkce`** | `defaults` | Toggle PKCE support
246
+ **`response`** | `defaults` | Response data to receive
247
+ **`transport`** | `defaults` | A way to deliver the response data
248
+ **`callback`** | `[provider]` | Relative or absolute URL to receive the response data
249
+ **`overrides`** | `[provider]` | Static configuration overrides for a provider
250
+ **`dynamic`** | `[provider]` | Configuration keys that can be overridden dynamically over HTTP
251
+ ***Client App*** |
252
+ **`key`** **`client_id`** **`consumer_key`** | `[provider]` | The `client_id` or `consumer_key` of your OAuth app
253
+ **`secret`** **`client_secret`** **`consumer_secret`** | `[provider]` | The `client_secret` or `consumer_secret` of your OAuth app
254
+ **`scope`** | `[provider]` | List of scopes to request
255
+ **`custom_params`** | `[provider]` | Custom authorization parameters and their values
256
+ **`subdomain`** | `[provider]` | String to embed into the authorization server URLs
257
+ **`public_key`** | `[provider]` | Public PEM or JWK
258
+ **`private_key`** | `[provider]` | Private PEM or JWK
259
+ **`redirect_uri`** | `generated` | Absolute redirect URL of the OAuth app
260
+ ***Grant*** |
261
+ **`name`** | `generated` | Provider's [name](#grant)
262
+ **`[provider]`** | `generated` | Provider's [name](#grant) as key
263
+ **`profile_url`** | [profile.json] | User profile URL
264
+
265
+
266
+ ## Configuration: Values
267
+
268
+ Key | Location | Value
269
+ :- | :-: | :-:
270
+ ***Authorization Server*** |
271
+ **`request_url`** | [oauth.json] | `'https://api.twitter.com/oauth/request_token'`
272
+ **`authorize_url`** | [oauth.json] | `'https://api.twitter.com/oauth/authenticate'`
273
+ **`access_url`** | [oauth.json] | `'https://api.twitter.com/oauth/access_token'`
274
+ **`oauth`** | [oauth.json] | `2` `1`
275
+ **`scope_delimiter`** | [oauth.json] | `','` `' '`
276
+ **`token_endpoint_auth_method`** | `[provider]` | `'client_secret_post'` `'client_secret_basic'` `'private_key_jwt'`
277
+ **`token_endpoint_auth_signing_alg`** | `[provider]` | `'RS256'` `'ES256'` `'PS256'`
278
+ ***Client Server*** |
279
+ **`origin`** | `defaults` | `'http://localhost:3000'` `https://site.com`
280
+ **`prefix`** | `defaults` | `'/connect'` `/oauth` `''`
281
+ **`state`** | `defaults` | `true`
282
+ **`nonce`** | `defaults` | `true`
283
+ **`pkce`** | `defaults` | `true`
284
+ **`response`** | `defaults` | `['tokens', 'raw', 'jwt', 'profile']`
285
+ **`transport`** | `defaults` | `'querystring'` `'session'` `'state'`
286
+ **`callback`** | `[provider]` | `'/hello'` `'https://site.com/hi'`
287
+ **`overrides`** | `[provider]` | `{something: {scope: ['..']}}`
288
+ **`dynamic`** | `[provider]` | `['scope', 'subdomain']`
289
+ ***Client App*** |
290
+ **`key`** **`client_id`** **`consumer_key`** | `[provider]` | `'123'`
291
+ **`secret`** **`client_secret`** **`consumer_secret`** | `[provider]` | `'123'`
292
+ **`scope`** | `[provider]` | `['openid', '..']`
293
+ **`custom_params`** | `[provider]` | `{access_type: 'offline'}`
294
+ **`subdomain`** | `[provider]` | `'myorg'`
295
+ **`public_key`** | `[provider]` | `'..PEM..'` `'{..JWK..}'`
296
+ **`private_key`** | `[provider]` | `'..PEM..'` `'{..JWK..}'`
297
+ **`redirect_uri`** |`generated` | `'http://localhost:3000/connect/twitter/callback'`
298
+ ***Grant*** |
299
+ **`name`** |`generated` | `name: 'twitter'`
300
+ **`[provider]`** |`generated` | `twitter: true`
301
+ **`profile_url`** | [profile.json] | `'https://api.twitter.com/1.1/users/show.json'`
302
+
303
+
304
+ ## Configuration: Scopes
305
+
306
+ Grant relies on configuration gathered from **6** different places:
307
+
308
+ 1. The **first** place Grant looks for configuration is the built-in [oauth.json] file located in the config folder.
309
+
310
+ 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.
311
+
312
+ 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.
313
+
314
+ 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.
315
+
316
+ 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.
317
+
318
+ 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.
319
+
320
+ ---
321
+
322
+ # Connect
323
+
324
+
325
+ ## Connect: Origin
326
+
327
+ The `origin` is where your client server can be reached:
328
+
329
+ ```json
330
+ {
331
+ "defaults": {
332
+ "origin": "http://localhost:3000"
333
+ }
334
+ }
335
+ ```
336
+
337
+ 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.
338
+
339
+ ## Connect: Prefix
340
+
341
+ By default Grant operates on the following two routes:
342
+
343
+ ```
344
+ /connect/:provider/:override?
345
+ /connect/:provider/callback
346
+ ```
347
+
348
+ However, the default `/connect` prefix can be configured:
349
+
350
+ ```json
351
+ {
352
+ "defaults": {
353
+ "origin": "http://localhost:3000",
354
+ "prefix": "/oauth"
355
+ }
356
+ }
357
+ ```
358
+
359
+
360
+ ## Connect: Redirect URI
361
+
362
+ The [`redirect_uri`](#misc-redirect-uri) of your OAuth app should follow this format:
363
+
364
+ ```
365
+ [origin][prefix]/[provider]/callback
366
+ ```
367
+
368
+ 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.
369
+
370
+ For example: `http://localhost:3000/connect/google/callback`
371
+
372
+ 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.
373
+
374
+
375
+ ## Connect: Custom Parameters
376
+
377
+ Some providers may employ custom authorization parameters that you can configure using the `custom_params` key:
378
+
379
+ ```json
380
+ {
381
+ "google": {
382
+ "custom_params": {"access_type": "offline", "prompt": "consent"}
383
+ },
384
+ "reddit": {
385
+ "custom_params": {"duration": "permanent"}
386
+ },
387
+ "trello": {
388
+ "custom_params": {"name": "my app", "expiration": "never"}
389
+ }
390
+ }
391
+ ```
392
+
393
+
394
+ ## Connect: OpenID Connect
395
+
396
+ The `openid` scope is required, and generating a random `nonce` string is optional but recommended:
397
+
398
+ ```json
399
+ {
400
+ "google": {
401
+ "scope": ["openid"],
402
+ "nonce": true
403
+ }
404
+ }
405
+ ```
406
+
407
+ Grant **does not** verify the signature of the returned `id_token` by default.
408
+
409
+ However, the following two claims of the `id_token` are being validated:
410
+
411
+ 1. `aud` - is the token intended for my OAuth app?
412
+ 2. `nonce` - does it tie to a request of my own?
413
+
414
+
415
+ ## Connect: PKCE
416
+
417
+ PKCE can be enabled for all providers or for a specific provider only:
418
+
419
+ ```json
420
+ {
421
+ "google": {
422
+ "pkce": true
423
+ }
424
+ }
425
+ ```
426
+
427
+ Providers that do not support PKCE will ignore the additional parameters being sent.
428
+
429
+
430
+ ## Connect: Static Overrides
431
+
432
+ Provider sub configurations can be configured using the `overrides` key:
433
+
434
+ ```json
435
+ {
436
+ "github": {
437
+ "key": "...", "secret": "...",
438
+ "scope": ["public_repo"],
439
+ "callback": "/hello",
440
+ "overrides": {
441
+ "notifications": {
442
+ "key": "...", "secret": "...",
443
+ "scope": ["notifications"]
444
+ },
445
+ "all": {
446
+ "scope": ["repo", "gist", "user"],
447
+ "callback": "/hey"
448
+ }
449
+ }
450
+ }
451
+ }
452
+ ```
453
+
454
+ Navigate to:
455
+
456
+ - `/connect/github` to request the public_repo `scope`
457
+ - `/connect/github/notifications` to request the notifications `scope` using another OAuth App (`key` and `secret`)
458
+ - `/connect/github/all` to request a bunch of `scope`s and also receive the response data in another `callback` route
459
+
460
+ ---
461
+
462
+ # Callback
463
+
464
+
465
+ ## Callback: Data
466
+
467
+ By default the response data will be returned in your `callback` route or absolute URL encoded as querystring.
468
+
469
+ Depending on the [`transport`](#callback-transport) being used the response data can be returned in the `session` or in the `state` object instead.
470
+
471
+ The amount of the returned data can be controlled by using the [`response`](#callback-response) configuration.
472
+
473
+ ### OAuth 2.0
474
+
475
+ ```js
476
+ {
477
+ id_token: '...',
478
+ access_token: '...',
479
+ refresh_token: '...',
480
+ raw: {
481
+ id_token: '...',
482
+ access_token: '...',
483
+ refresh_token: '...',
484
+ some: 'other data'
485
+ }
486
+ }
487
+ ```
488
+
489
+ The `refresh_token` is optional. The `id_token` is returned only for [OpenID Connect](#connect-openid-connect) providers requesting the `openid` scope.
490
+
491
+
492
+ ### OAuth 1.0a
493
+
494
+ ```js
495
+ {
496
+ access_token: '...',
497
+ access_secret: '...',
498
+ raw: {
499
+ oauth_token: '...',
500
+ oauth_token_secret: '...',
501
+ some: 'other data'
502
+ }
503
+ }
504
+ ```
505
+
506
+
507
+ ### Error
508
+
509
+ ```js
510
+ {
511
+ error: {
512
+ some: 'error data'
513
+ }
514
+ }
515
+ ```
516
+
517
+
518
+ ## Callback: Transport
519
+
520
+ ### querystring
521
+
522
+ By default Grant will encode the OAuth [response data](#callback-data) as `querystring` in your `callback` route or absolute URL:
523
+
524
+ ```json
525
+ {
526
+ "github": {
527
+ "callback": "https://site.com/hello"
528
+ }
529
+ }
530
+ ```
531
+
532
+ This is useful when using Grant as [OAuth Proxy](#dynamic-oauth-proxy). However this final `https://site.com/hello?access_token=...` redirect potentially may leak private data in your server logs, especially when sitting behind a reverse proxy.
533
+
534
+
535
+ ### session
536
+
537
+ For local `callback` routes the session `transport` is recommended:
538
+
539
+ ```json
540
+ {
541
+ "defaults": {
542
+ "transport": "session"
543
+ },
544
+ "github": {
545
+ "callback": "/hello"
546
+ }
547
+ }
548
+ ```
549
+
550
+ This will make the OAuth [response data](#callback-data) available in the `session` object instead:
551
+
552
+ ```js
553
+ req.session.grant.response // Express
554
+ ctx.session.grant.response // Koa
555
+ req.yar.get('grant').response // Hapi
556
+ req.session.grant.response // Fastify
557
+ (await session.get()).grant.response // Serverless Function
558
+ ```
559
+
560
+
561
+ ### state
562
+
563
+ The request/response lifecycle `state` can be used as well:
564
+
565
+ ```json
566
+ {
567
+ "defaults": {
568
+ "transport": "state"
569
+ }
570
+ }
571
+ ```
572
+
573
+ 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:
574
+
575
+ ```js
576
+ res.locals.grant.response // Express
577
+ ctx.state.grant.response // Koa
578
+ req.plugins.grant.response // Hapi
579
+ res.grant.response // Fastify
580
+ var {response} = await grant(...) // Serverless Function
581
+ ```
582
+
583
+
584
+ ## Callback: Response
585
+
586
+ By default Grant returns all of the available tokens and the `raw` response data returned by the Authorization server:
587
+
588
+ ```js
589
+ {
590
+ id_token: '...',
591
+ access_token: '...',
592
+ refresh_token: '...',
593
+ raw: {
594
+ id_token: '...',
595
+ access_token: '...',
596
+ refresh_token: '...',
597
+ some: 'other data'
598
+ }
599
+ }
600
+ ```
601
+
602
+ ### querystring
603
+
604
+ When using the querystring [`transport`](#callback-transport) it might be a good idea to limit the response data:
605
+
606
+ ```json
607
+ {
608
+ "defaults": {
609
+ "response": ["tokens"]
610
+ }
611
+ }
612
+ ```
613
+
614
+ This will return only the tokens available, without the `raw` response data.
615
+
616
+ 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.
617
+
618
+ ### session
619
+
620
+ Using the session [`transport`](#callback-transport) is generally safer, but it also depends on the implementation of your session store.
621
+
622
+ 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.
623
+
624
+ ```json
625
+ {
626
+ "google": {
627
+ "response": ["tokens"]
628
+ }
629
+ }
630
+ ```
631
+
632
+ This will return only the tokens available, without the `raw` response data.
633
+
634
+ ### jwt
635
+
636
+ 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`:
637
+
638
+ ```json
639
+ {
640
+ "google": {
641
+ "response": ["tokens", "raw", "jwt"]
642
+ }
643
+ }
644
+ ```
645
+
646
+ This will make the decoded JWT available in the response data:
647
+
648
+ ```js
649
+ {
650
+ id_token: '...',
651
+ access_token: '...',
652
+ refresh_token: '...',
653
+ raw: {
654
+ id_token: '...',
655
+ access_token: '...',
656
+ refresh_token: '...',
657
+ some: 'other data'
658
+ },
659
+ jwt: {id_token: {header: {}, payload: {}, signature: '...'}}
660
+ }
661
+ ```
662
+
663
+ Make sure you include all of the response keys that you want to be returned when configuring the `response` data explicitly.
664
+
665
+
666
+ ### profile
667
+
668
+ Outside of the regular OAuth flow, Grant can also request the user profile:
669
+
670
+ ```json
671
+ {
672
+ "google": {
673
+ "response": ["tokens", "profile"]
674
+ }
675
+ }
676
+ ```
677
+
678
+ Additionaly a `profile` key will be available in the response data:
679
+
680
+ ```js
681
+ {
682
+ access_token: '...',
683
+ refresh_token: '...',
684
+ profile: {some: 'user data'}
685
+ }
686
+ ```
687
+
688
+ The `profile` key contains either the raw response data returned by the user profile endpoint or an error message.
689
+
690
+ 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 `scope`s were requested.
691
+
692
+
693
+ ## Callback: Session
694
+
695
+ 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.
696
+
697
+ Once you receive the [response data](#callback-data) in your `callback` route you are free to destroy that session.
698
+
699
+ However, there are a few session keys returned in your `callback` route, that you may find useful:
700
+
701
+ Key | Availability | Description
702
+ :-- | :-- | :--
703
+ `provider` | **Always** | The provider [name](#grant) used for this authorization
704
+ `override` | Depends on URL | The [static override](#connect-static-overrides) name used for this authorization
705
+ `dynamic` | Depends on request type | The [dynamic override](#dynamic-http) configuration passed to this authorization
706
+ `state` | OAuth 2.0 only | OAuth 2.0 state string that was generated
707
+ `nonce` | OpenID Connect only | [OpenID Connect](#connect-openid-connect) nonce string that was generated
708
+ `code_verifier` | PKCE only | The code verifier that was generated for [PKCE](#connect-pkce)
709
+ `request` | OAuth 1.0a only | Data returned from the first request of the OAuth 1.0a flow
710
+ `response` | Depends on transport used | The final [response data](#callback-data)
711
+
712
+ ---
713
+
714
+ # Dynamic Configuration
715
+
716
+
717
+ ## Dynamic: Instance
718
+
719
+ Every Grant instance have a `config` property attached to it:
720
+
721
+ ```js
722
+ var grant = Grant(require('./config'))
723
+ console.log(grant.config)
724
+ ```
725
+
726
+ You can use the `config` property to alter the Grant's behavior during runtime without having to restart your server.
727
+
728
+ This property contains the **generated** configuration used internally by Grant, and changes made to that configuration affects the **entire** Grant instance!
729
+
730
+
731
+ ## Dynamic: State
732
+
733
+ The request/response lifecycle state can be used to alter configuration on every request:
734
+
735
+ ```js
736
+ var state = {dynamic: {subdomain: 'usershop'}}
737
+ res.locals.grant = state // Express
738
+ ctx.state.grant = state // Koa
739
+ req.plugins.grant = state // Hapi
740
+ req.grant = state // Fastify
741
+ await grant(..., state) // Serverless Function
742
+ ```
743
+
744
+ 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.
745
+
746
+ The request/response lifecycle state is not controlled by the [`dynamic`](#dynamic-http) configuration, meaning that you can override any configuration key.
747
+
748
+ Any allowed [`dynamic`](#dynamic-http) configuration key sent through HTTP GET/POST request will override the identical one set using a state override.
749
+
750
+
751
+ ## Dynamic: HTTP
752
+
753
+ The `dynamic` configuration allows certain configuration keys to be set dynamically over HTTP GET/POST request.
754
+
755
+ 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:
756
+
757
+ ```json
758
+ {
759
+ "shopify": {
760
+ "dynamic": ["subdomain"]
761
+ }
762
+ }
763
+ ```
764
+
765
+ Then you can have a web form on your website allowing the user to specify the shop name:
766
+
767
+ ```html
768
+ <form action="/connect/shopify" method="POST" accept-charset="utf-8">
769
+ <input type="text" name="subdomain" value="" />
770
+ <button>Login</button>
771
+ </form>
772
+ ```
773
+
774
+ Making a `POST` request to the `/connect/:provider/:override?` route requires a form body parser middleware:
775
+
776
+ ```js
777
+ .use(require('body-parser').urlencoded({extended: true})) // Express
778
+ .use(require('koa-bodyparser')()) // Koa
779
+ .register(require('@fastify/formbody')) // Fastify
780
+ ```
781
+
782
+ Alternatively you can make a `GET` request to the `/connect/:provider/:override?` route:
783
+
784
+ ```
785
+ https://awesome.com/connect/shopify?subdomain=usershop
786
+ ```
787
+
788
+ Any `dynamic` configuration sent over HTTP GET/POST request overrides any other configuration.
789
+
790
+
791
+ ## Dynamic: OAuth Proxy
792
+
793
+ In case you really want to, you can allow `dynamic` configuration override of every configuration key for a provider:
794
+
795
+ ```json
796
+ {
797
+ "github": {
798
+ "dynamic": true
799
+ }
800
+ }
801
+ ```
802
+
803
+ And the most extreme case is allowing even non preconfigured providers to be used dynamically:
804
+
805
+ ```json
806
+ {
807
+ "defaults": {
808
+ "dynamic": true
809
+ }
810
+ }
811
+ ```
812
+
813
+ Essentially Grant is a completely transparent **[OAuth Proxy][oauth-like-a-boss]**.
814
+
815
+ ---
816
+
817
+ # Misc
818
+
819
+ ## Misc: Redirect URI
820
+
821
+ The [`origin`](#connect-origin) and the [`prefix`](#connect-prefix) configuration is used to generate the correct [`redirect_uri`](#connect-redirect-uri) that Grant expects:
822
+
823
+ ```json
824
+ {
825
+ "defaults": {
826
+ "origin": "https://mysite.com"
827
+ },
828
+ "google": {},
829
+ "twitter": {}
830
+ }
831
+ ```
832
+
833
+ The above configuration is identical to:
834
+
835
+ ```json
836
+ {
837
+ "google": {
838
+ "redirect_uri": "https://mysite.com/connect/google/callback"
839
+ },
840
+ "twitter": {
841
+ "redirect_uri": "https://mysite.com/connect/twitter/callback"
842
+ }
843
+ }
844
+ ```
845
+
846
+ Explicitly specifying the `redirect_uri` overrides the one generated by default.
847
+
848
+
849
+ ## Misc: Custom Providers
850
+
851
+ 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:
852
+
853
+ ```json
854
+ {
855
+ "defaults": {
856
+ "origin": "http://localhost:3000"
857
+ },
858
+ "awesome": {
859
+ "authorize_url": "https://awesome.com/authorize",
860
+ "access_url": "https://awesome.com/token",
861
+ "oauth": 2,
862
+ "key": "...",
863
+ "secret": "...",
864
+ "scope": ["read", "write"]
865
+ }
866
+ }
867
+ ```
868
+
869
+ Take a look at the [oauth.json] file on how various providers are being configured.
870
+
871
+
872
+ ## Misc: Meta Configuration
873
+
874
+ You can document your configuration by adding custom keys to it:
875
+
876
+ ```json
877
+ {
878
+ "google": {
879
+ "meta": {
880
+ "app": "My Awesome OAuth App",
881
+ "owner": "my_email@gmail.com",
882
+ "url": "https://url/to/manage/oauth/app"
883
+ }
884
+ }
885
+ }
886
+ ```
887
+
888
+ Note that `meta` is arbitrary key, but it cannot be one of the [reserved keys][reserved-keys].
889
+
890
+
891
+ ## Misc: Handler Constructors
892
+
893
+ Grant supports different ways of instantiation:
894
+
895
+ ```js
896
+ // Express or any other handler
897
+ var grant = require('grant').express()(config)
898
+ var grant = require('grant').express()({config, ...})
899
+ var grant = require('grant').express(config)
900
+ var grant = require('grant').express({config, ...})
901
+ var grant = require('grant')({handler: 'express', config, ...})
902
+ ```
903
+
904
+ Using the `new` keyword is optional:
905
+
906
+ ```js
907
+ var Grant = require('grant').express()
908
+ var grant = Grant(config)
909
+ var grant = new Grant(config)
910
+ ```
911
+
912
+ Additionally Hapi accepts the configuration in two different ways:
913
+
914
+ ```js
915
+ server.register([{plugin: grant(config)}])
916
+ server.register([{plugin: grant(), options: config}])
917
+ ```
918
+
919
+ ## Misc: Path Prefix
920
+
921
+ You can mount Grant under specific path prefix:
922
+
923
+ ```js
924
+ // Express
925
+ app.use('/oauth', grant(config))
926
+ // Koa - using koa-mount
927
+ app.use(mount('/oauth', grant(config)))
928
+ // Hapi
929
+ server.register([{routes: {prefix: '/oauth'}, plugin: grant(config)}])
930
+ // Fastify
931
+ server.register(grant(config), {prefix: '/oauth'})
932
+ ```
933
+
934
+ In this case the [`prefix`](#connect-prefix) configuration should reflect that + any other path parts that you may have:
935
+
936
+ ```json
937
+ {
938
+ "defaults": {
939
+ "origin": "http://localhost:3000",
940
+ "prefix": "/oauth/login"
941
+ }
942
+ }
943
+ ```
944
+
945
+ In this case you login by navigating to: `http://localhost:3000/oauth/login/:provider`
946
+
947
+ And the [`redirect_uri`](#connect-redirect-uri) of your OAuth app should be `http://localhost:3000/oauth/login/:provider/callback`
948
+
949
+ Optionally you can prefix your [`callback`](#callback) routes as well:
950
+
951
+ ```json
952
+ {
953
+ "github": {
954
+ "callback": "/oauth/login/hello"
955
+ }
956
+ }
957
+ ```
958
+
959
+ ## Misc: Request
960
+
961
+ The underlying [HTTP client] can be configured using the `request` option:
962
+
963
+ ```js
964
+ var grant = require('grant').express({
965
+ config,
966
+ request: {agent, timeout: 5000}
967
+ })
968
+ ```
969
+
970
+ Fancy [request logs] are available too:
971
+
972
+ ```bash
973
+ npm i --save-dev request-logs
974
+ DEBUG=req,res,json node app.js
975
+ ```
976
+
977
+ ## Misc: ES Modules and TypeScript
978
+
979
+ Import Grant in your `.mjs` files:
980
+
981
+ ```js
982
+ import express from 'express'
983
+ import session from 'express-session'
984
+ import grant from 'grant'
985
+ import config from './config.json'
986
+
987
+ express()
988
+ .use(session({}))
989
+ .use(grant.express(config))
990
+ ```
991
+
992
+ Importing a `.json` file may require additional flag:
993
+
994
+ ```bash
995
+ node --experimental-json-modules app.mjs
996
+ ```
997
+
998
+ Grant ships with extensive [type definitions][type-definitions] for TypeScript. Additonal type definitions and examples can be found [here][grant-types].
999
+
1000
+
1001
+ ## Misc: OAuth Quirks
1002
+
1003
+ ### Subdomain URLs
1004
+
1005
+ 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.
1006
+
1007
+ The `subdomain` option can be used to specify your company name, server region etc:
1008
+
1009
+ ```json
1010
+ "shopify": {
1011
+ "subdomain": "mycompany"
1012
+ },
1013
+ "battlenet": {
1014
+ "subdomain": "us"
1015
+ }
1016
+ ```
1017
+
1018
+ Then Grant will generate the correct OAuth URLs:
1019
+
1020
+ ```json
1021
+ "shopify": {
1022
+ "authorize_url": "https://mycompany.myshopify.com/admin/oauth/authorize",
1023
+ "access_url": "https://mycompany.myshopify.com/admin/oauth/access_token"
1024
+ },
1025
+ "battlenet": {
1026
+ "authorize_url": "https://us.battle.net/oauth/authorize",
1027
+ "access_url": "https://us.battle.net/oauth/token"
1028
+ }
1029
+ ```
1030
+
1031
+ Alternatively you can override the entire `authorize_url` and `access_url` in your configuration.
1032
+
1033
+
1034
+ ### Sandbox OAuth URLs
1035
+
1036
+ 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):
1037
+
1038
+ ```json
1039
+ "paypal": {
1040
+ "authorize_url": "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
1041
+ "access_url": "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice"
1042
+ },
1043
+ "evernote": {
1044
+ "request_url": "https://sandbox.evernote.com/oauth",
1045
+ "authorize_url": "https://sandbox.evernote.com/OAuth.action",
1046
+ "access_url": "https://sandbox.evernote.com/oauth"
1047
+ }
1048
+ ```
1049
+
1050
+
1051
+ ### Sandbox Redirect URI
1052
+
1053
+ Very rarely you may need to override the [`redirect_uri`](#connect-redirect-uri) that Grant generates for you.
1054
+
1055
+ 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:
1056
+
1057
+ ```json
1058
+ "feedly": {
1059
+ "redirect_uri": "http://localhost"
1060
+ }
1061
+ ```
1062
+
1063
+ 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:
1064
+
1065
+ ```js
1066
+ var qs = require('querystring')
1067
+
1068
+ app.get('/', (req, res) => {
1069
+ if (process.env.NODE_ENV === 'development' &&
1070
+ req.session.grant &&
1071
+ req.session.grant.provider === 'feedly' &&
1072
+ req.query.code
1073
+ ) {
1074
+ res.redirect(`/connect/${req.session.grant.provider}/callback?${qs.stringify(req.query)}`)
1075
+ }
1076
+ })
1077
+ ```
1078
+
1079
+ As usual you will receive the response data in your final [`callback`](#callback) route.
1080
+
1081
+
1082
+ ### Provider Quirks
1083
+
1084
+
1085
+ > **Ebay**
1086
+
1087
+ 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:
1088
+
1089
+ ```json
1090
+ "ebay": {
1091
+ "redirect_uri": "RUNAME"
1092
+ }
1093
+ ```
1094
+
1095
+
1096
+ > **Flickr, Freelancer, Optimizely**
1097
+
1098
+ 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:
1099
+
1100
+ ```json
1101
+ "flickr": {
1102
+ "scope": ["write"]
1103
+ },
1104
+ "freelancer": {
1105
+ "scope": ["1", "2"]
1106
+ },
1107
+ "optimizely": {
1108
+ "scope": ["all"]
1109
+ }
1110
+ ```
1111
+
1112
+
1113
+ > **Mastodon**
1114
+
1115
+ Mastodon requires the entire domain of your server to be embedded in the OAuth URLs. However you should use the `subdomain` option:
1116
+
1117
+ ```json
1118
+ "mastodon": {
1119
+ "subdomain": "mastodon.cloud"
1120
+ }
1121
+ ```
1122
+
1123
+
1124
+ > **Openstreetmap**
1125
+
1126
+
1127
+ Openstreetmap OAuth 2.0 applications have to use the `openstreetmap2` provider:
1128
+
1129
+ ```json
1130
+ "openstreetmap2": {
1131
+ "state": true,
1132
+ "scope": [
1133
+ "openid",
1134
+ "read_prefs"
1135
+ ]
1136
+ }
1137
+ ```
1138
+
1139
+
1140
+ > **SurveyMonkey**
1141
+
1142
+ Set your Mashery user name as `key` and your application key as `api_key`:
1143
+
1144
+ ```json
1145
+ "surveymonkey": {
1146
+ "key": "MASHERY_USER_NAME",
1147
+ "secret": "CLIENT_SECRET",
1148
+ "custom_params": {"api_key": "CLIENT_ID"}
1149
+ }
1150
+ ```
1151
+
1152
+
1153
+ > **Twitter**
1154
+
1155
+ Twitter OAuth 1.0a custom scope parameter can be specified in two ways:
1156
+
1157
+ ```json
1158
+ "twitter": {
1159
+ "custom_params": {"x_auth_access_type": "read"}
1160
+ }
1161
+ "twitter": {
1162
+ "scope": ["read"]
1163
+ }
1164
+ ```
1165
+
1166
+ Twitter OAuth 2.0 applications have to use the `twitter2` provider:
1167
+
1168
+ ```json
1169
+ "twitter2": {
1170
+ "state": true,
1171
+ "pkce": true,
1172
+ "scope": [
1173
+ "users.read",
1174
+ "tweet.read"
1175
+ ]
1176
+ }
1177
+ ```
1178
+
1179
+
1180
+ > **VisualStudio**
1181
+
1182
+ Set your Client Secret as `secret` not the App Secret:
1183
+
1184
+ ```json
1185
+ "visualstudio": {
1186
+ "key": "APP_ID",
1187
+ "secret": "CLIENT_SECRET instead of APP_SECRET"
1188
+ }
1189
+ ```
1190
+
1191
+ ---
1192
+
1193
+
1194
+ [npm-version]: https://img.shields.io/npm/v/grant.svg?style=flat-square (NPM Version)
1195
+ [test-ci-img]: https://img.shields.io/github/actions/workflow/status/simov/grant/test.yml?style=flat-square (Build Status)
1196
+ [test-cov-img]: https://img.shields.io/coveralls/simov/grant.svg?style=flat-square (Test Coverage)
1197
+ [snyk-vulnerabilities]: https://img.shields.io/badge/vulnerabilities-0-geen?style=flat-square (Vulnerabilities)
1198
+
1199
+ [npm]: https://www.npmjs.com/package/grant
1200
+ [test-ci-url]: https://github.com/simov/grant/actions/workflows/test.yml
1201
+ [test-cov-url]: https://coveralls.io/r/simov/grant?branch=master
1202
+ [snyk]: https://snyk.io/test/npm/grant
1203
+
1204
+ [grant-oauth]: https://grant.outofindex.com
1205
+ [oauth-like-a-boss]: https://dev.to/simov/oauth-like-a-boss-2m3b
1206
+ [http client]: https://github.com/simov/request-compose
1207
+ [request logs]: https://github.com/simov/request-logs
1208
+
1209
+ [oauth.json]: https://github.com/simov/grant/blob/master/config/oauth.json
1210
+ [profile.json]: https://github.com/simov/grant/blob/master/config/profile.json
1211
+ [reserved-keys]: https://github.com/simov/grant/blob/master/config/reserved.json
1212
+ [examples]: https://github.com/simov/grant/tree/master/examples
1213
+ [changelog]: https://github.com/simov/grant/blob/master/CHANGELOG.md
1214
+ [migration]: https://github.com/simov/grant/blob/master/MIGRATION.md
1215
+
1216
+ [grant-aws]: https://github.com/simov/grant-aws
1217
+ [grant-azure]: https://github.com/simov/grant-azure
1218
+ [grant-gcloud]: https://github.com/simov/grant-gcloud
1219
+ [grant-vercel]: https://github.com/simov/grant-vercel
1220
+
1221
+ [grant-types]: https://github.com/simov/grant-types
1222
+ [type-definitions]: https://github.com/simov/grant/blob/master/grant.d.ts