OAuth Proxy

Overview

Grant

npm-version travis-ci coveralls-status

OAuth Proxy

200+ Supported Providers / OAuth Playground

23andme | 500px | acton | acuityscheduling | aha | alchemer | amazon | angellist | apple | arcgis | asana | assembla | atlassian | auth0 | authentiq | autodesk | aweber | axosoft | baidu | basecamp | battlenet | beatport | bitbucket | bitly | box | buffer | campaignmonitor | cas | cheddar | clio | cognito | coinbase | concur | constantcontact | coursera | dailymotion | deezer | delivery | deputy | deviantart | digitalocean | discogs | discord | disqus | docusign | dribbble | dropbox | ebay | echosign | ecwid | edmodo | egnyte | etsy | eventbrite | evernote | eyeem | facebook | familysearch | feedly | figma | fitbit | flattr | flickr | flowdock | formstack | foursquare | freeagent | freelancer | freshbooks | fusionauth | garmin | geeklist | genius | getbase | getpocket | gitbook | github | gitlab | gitter | goodreads | google | groove | gumroad | harvest | hellosign | heroku | homeaway | hootsuite | huddle | ibm | iconfinder | idme | idonethis | imgur | infusionsoft | instagram | intuit | jamendo | jumplead | kakao | keycloak | line | linkedin | live | livechat | logingov | lyft | mailchimp | mailup | mailxpert | mapmyfitness | mastodon | medium | meetup | mendeley | mention | microsoft | mixcloud | moxtra | myob | naver | nest | netlify | nokotime | nylas | okta | onelogin | openstreetmap | optimizely | patreon | paypal | phantauth | pinterest | plurk | podio | producthunt | projectplace | pushbullet | qq | ravelry | redbooth | reddit | runkeeper | salesforce | shoeboxed | shopify | skyrock | slack | slice | smartsheet | smugmug | snapchat | snowflake | socialpilot | socrata | soundcloud | spotify | square | stackexchange | stocktwits | stormz | storyblok | strava | stripe | surveymonkey | thingiverse | ticketbud | timelyapp | todoist | trakt | traxo | trello | tripit | tumblr | twitch | twitter | typeform | uber | underarmour | unsplash | upwork | uservoice | vend | venmo | vercel | verticalresponse | viadeo | vimeo | visualstudio | vk | wechat | weekdone | weibo | withings | wordpress | wrike | xero | xing | yahoo | yammer | yandex | zendesk | zoom

Table of Contents

Migration Guide: from v4 to v5


Handlers

HTTP Frameworks

Express
var express = require('express')
var session = require('express-session')
var grant = require('grant').express()

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

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

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

fastify()
  .register(cookie)
  .register(session, {secret: 'grant', cookie: {secure: false}})
  .register(grant({/*configuration - see below*/}))

Serverless Functions

AWS Lambda
var grant = require('grant').aws({
  config: {/*configuration - see below*/}, session: {secret: 'grant'}
})

exports.handler = async (event) => {
  var {redirect, response} = await grant(event)
  return redirect || {
    statusCode: 200,
    headers: {'content-type': 'application/json'},
    body: JSON.stringify(response)
  }
}
Azure Function
var grant = require('grant').azure({
  config: {/*configuration - see below*/}, session: {secret: 'grant'}
})

module.exports = async (context, req) => {
  var {redirect, response} = await grant(req)
  return redirect || {
    status: 200,
    headers: {'content-type': 'application/json'},
    body: JSON.stringify(response)
  }
}
Google Cloud Function
var grant = require('grant').gcloud({
  config: {/*configuration - see below*/}, session: {secret: 'grant'}
})

exports.handler = async (req, res) => {
  var {response} = await grant(req, res)
  if (response) {
    res.statusCode = 200
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify(response))
  }
}
Vercel
var grant = require('grant').vercel({
  config: {/*configuration - see below*/}, session: {secret: 'grant'}
})

module.exports = async (req, res) => {
  var {response} = await grant(req, res)
  if (response) {
    res.statusCode = 200
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify(response))
  }
}

Examples

express / koa / hapi / fastify / aws / azure / gcloud / vercel

ES Modules and TypeScript


Configuration

Configuration: Basics

{
  "defaults": {
    "origin": "http://localhost:3000",
    "transport": "session",
    "state": true
  },
  "google": {
    "key": "...",
    "secret": "...",
    "scope": ["openid"],
    "nonce": true,
    "custom_params": {"access_type": "offline"},
    "callback": "/hello"
  },
  "twitter": {
    "key": "...",
    "secret": "...",
    "callback": "/hi"
  }
}
  • defaults - default configuration for all providers
    • origin - where your client server can be reached http://localhost:3000 | https://site.com ...
    • transport - a transport used to deliver the response data in your callback route
    • state - generate random state string
  • provider - any supported provider google | twitter ...
    • key - consumer_key or client_id of your OAuth app
    • secret - consumer_secret or client_secret of your OAuth app
    • scope - array of OAuth scopes to request
    • nonce - generate random nonce string (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. 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 HTTP override.


Connect

Connect: Origin

The origin is where your client server can be reached:

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

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:

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

However, the default /connect prefix can be configured:

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

Connect: Redirect URI

The redirect_uri of your OAuth app should follow this format:

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

Where origin and prefix have to match the ones set in your configuration, and provider 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 being used you will receive the response data in the callback 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:

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

Connect: OpenID Connect

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

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

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

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

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

Connect: PKCE

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

{
  "google": {
    "pkce": true
  }
}

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

Connect: Static Overrides

Provider sub configurations can be configured using the overrides key:

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

Navigate to:

  • /connect/github to request the public_repo scope
  • /connect/github/notifications to request the notifications scope using another OAuth App (key and secret)
  • /connect/github/all to request a bunch of 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 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 through the response configuration.

OAuth 2.0

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

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

OAuth 1.0a

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

Error

{
  error: {
    some: 'error data'
  }
}

Callback: Transport

querystring

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

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

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

session

For local callback routes the session transport is recommended:

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

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

req.session.grant.response // Express
ctx.session.grant.response // Koa
req.yar.get('grant').response // Hapi
req.session.grant.response // Fastify
(await session.get()).grant.response // Serverless Function

state

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

{
  "defaults": {
    "transport": "state"
  }
}

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:

res.locals.grant.response // Express
ctx.state.grant.response // Koa
req.plugins.grant.response // Hapi
res.grant.response // Fastify
var {response} = await grant(...) // Serverless Function

Callback: Response

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

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

querystring

When using the querystring transport it might be a good idea to limit the response data:

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

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 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.

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

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 return id_token:

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

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

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

Make sure you include all 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:

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

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

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

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

Once you receive the response data in your callback route you are free to destroy that session.

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

Key Availability Description
provider Always The provider name 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:

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

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

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:

var state = {dynamic: {subdomain: 'usershop'}}
res.locals.grant = state // Express
ctx.state.grant = state // Koa
req.plugins.grant = state // Hapi
req.grant = state // Fastify
await grant(..., state) // Serverless Function

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 configuration, meaning that you can override any configuration key.

Any allowed dynamic 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 configuration key to be set dynamically:

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

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

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

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

.use(require('body-parser').urlencoded({extended: true})) // Express
.use(require('koa-bodyparser')()) // Koa
.register(require('fastify-formbody')) // Fastify

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

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

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:

{
  "github": {
    "dynamic": true
  }
}

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

{
  "defaults": {
    "dynamic": true
  }
}

Essentially Grant is a completely transparent OAuth Proxy.


Misc

Misc: Redirect URI

The origin and the prefix configuration is used to generate the correct redirect_uri that Grant expects:

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

The above configuration is identical to:

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

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:

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

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

Misc: Meta Configuration

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

{
  "google": {
    "meta": {
      "app": "My Awesome OAuth App",
      "owner": "[email protected]",
      "url": "https://url/to/manage/oauth/app"
    }
  }
}

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

Misc: Handler Constructors

Grant supports different ways of instantiation:

// Express or any other handler
var grant = require('grant').express()(config)
var grant = require('grant').express()({config, ...})
var grant = require('grant').express(config)
var grant = require('grant').express({config, ...})
var grant = require('grant')({handler: 'express', config, ...})

Using the new keyword is optional:

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

Additionally Hapi accepts the configuration in two different ways:

server.register([{plugin: grant(config)}])
server.register([{plugin: grant(), options: config}])

Misc: Path Prefix

You can mount Grant under specific path prefix:

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

In this case the prefix configuration should reflect that + any other path parts that you may have:

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

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

And the redirect_uri of your OAuth app should be http://localhost:3000/oauth/login/:provider/callback

Optionally you can prefix your callback routes as well:

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

Misc: Request

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

var grant = require('grant').express({
  config,
  request: {agent, timeout: 5000}
})

Fancy request logs are available too:

npm i --save-dev request-logs
DEBUG=req,res,json node app.js

Misc: ES Modules and TypeScript

Import Grant in your .mjs files:

import express from 'express'
import session from 'express-session'
import grant from 'grant'
import config from './config.json'

express()
  .use(session({}))
  .use(grant.express(config))

Importing a .json file may require additional flag:

node --experimental-json-modules app.mjs

Grant ships with extensive type definitions for TypeScript. Additonal type definitions and examples can be found here.

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:

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

Then Grant will generate the correct OAuth URLs:

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

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

Sandbox OAuth URLs

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

"paypal": {
  "authorize_url": "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
  "access_url": "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice"
},
"evernote": {
  "request_url": "https://sandbox.evernote.com/oauth",
  "authorize_url": "https://sandbox.evernote.com/OAuth.action",
  "access_url": "https://sandbox.evernote.com/oauth"
}

Sandbox Redirect URI

Very rarely you may need to override the redirect_uri that Grant generates for you.

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:

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

In this case you'll have to redirect the user to the [origin][prefix]/[provider]/callback route that Grant uses to execute the last step of the OAuth flow:

var qs = require('querystring')

app.get('/', (req, res) => {
  if (process.env.NODE_ENV === 'development' &&
      req.session.grant &&
      req.session.grant.provider === 'feedly' &&
      req.query.code
  ) {
    res.redirect(`/connect/${req.session.grant.provider}/callback?${qs.stringify(req.query)}`)
  }
})

As usual you will receive the response data in your final callback 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:

"ebay": {
  "redirect_uri": "RUNAME"
}

Flickr, Freelancer, Optimizely

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

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

Mastodon

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

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

SurveyMonkey

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

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

VisualStudio

Set your Client Secret as secret not the App Secret:

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

Comments
  • [Help] Example of Apple SignIn implementation with POST_FORM

    [Help] Example of Apple SignIn implementation with POST_FORM

    Hi 😄 ,

    I've would like to implement Apple SignIn authentication with Grant + Purest, but I'm unable to find great configuration with Purest (not update since 4 years with latest providers like 'Apple')

    Grant is unable to read POST_FORM access_token and it's return oAuth2 missing code parameter error.

    Grant config (not sure about this)

     apple: {
          key: '', // reverse.domain.name
          secret: '', // Need to be refer with "Generate client secret" 
          callback: `[domain.name]/auth/apple/callback`,
          nonce: true,
          state: true,
          scope: ['name', 'email'],
          custom_params: {
            response_type: 'code id_token',
            response_mode: 'form_post' // necessary... Apple constrain 😢 
          },
        },
    

    Purest configuration testing :

    const apple = purest({
            provider: 'apple',
            config: {
              apple: {
                'https://appleid.apple.com': {
                  __domain: {
                    auth: {
                      auth: { bearer: '[0]' },
                    },
                  },
                  '{endpoint}': {
                    __path: {
                      alias: '__default',
                    },
                  },
                },
              },
            }
          });
    
    apple
            .query()
            .post('auth/token') // not sure about this ? apple want post request only
            .auth(access_token)
            .request((err, res, body) => {  
                  if (err) {
                       callback(err);
                  } else {
                      // let do some stuff...
                      callback(null, {body});
                  }
            });
    
    

    I've successfully sign in with other libraries (apple-signin and apple-auth) but I would like to perform signIn request only with Grant + Purest and not implement a tierce package. But Grant return the same error with the access_token (code for Apple) and it's unable to return and read it from POST_FORM request.

    The project "Playground" implement Apple SignIn testing example, but I can't find any code to understand how it's working. Apple documentation is clearly... pitiable.

    Thanks for your help !

    opened by Lith 33
  • ES6 Modules with grant

    ES6 Modules with grant

    Hi. Thanks for making grant. I tried it and it works well for many OAuth2 providers.

    One question I had was regarding support for ES Modules. While I understand from the issues that you have decided not to support Typescript, I was wondering how to import grant as a ES6 module.

    While require('grant').fastify(); works, I wanted to go the standard route since require is not a standard and definitely not recommended in the future for Node.js with mjs support now in place.

    opened by tvvignesh 23
  • Support for Twitter API v2 with OAuth 2.0

    Support for Twitter API v2 with OAuth 2.0

    Instead of jumping back to the /callback, it goes directly to /token, so it looks like an internal bug, I've set up google, facebook, github, and they all work fine.

    I tried examples/response-profile, and it's the same issue

    I looked at the doc for twitter oauth 1.0a and found that twitter is different from other oauths in that its callback is a query string and what in the dashboard is set as a whitelist, so I'm guessing there's a miss-concating of the query string?

    Screen Shot 2022-01-17 at 1 15 59 AM

    Screen Shot 2022-01-17 at 1 32 19 AM

    2022-01-16T17:30:49.860Z *37920 @ElvisMini.local -  info: #req-6 ←GET:/oauth/twitter request from ip 127.0.0.1 (fastify-request-logger)
    2022-01-16T17:30:50.813Z *37920 @ElvisMini.local -  info: #req-6 →GET:/oauth/twitter response with a 302-status (fastify-request-logger)
    2022-01-16T17:30:50.821Z *37920 @ElvisMini.local -  info: #req-7 ←GET:/token request from ip 127.0.0.1 (fastify-request-logger)
    {
      provider: 'twitter',
      request: {
        error: {
          errors: [ { code: 32, message: 'Could not authenticate you.' } ]
        }
      },
      response: {
        error: {
          errors: [ { code: 32, message: 'Could not authenticate you.' } ]
        }
      }
    }
    2022-01-16T17:30:50.822Z *37920 @ElvisMini.local -  info: #req-7 →GET:/token response with a 200-status (fastify-request-logger)
    
    
    opened by ash0080 19
  • Oauth for Untappd not working

    Oauth for Untappd not working

    Hi all! I'm using Strapi for a personal BeerApp project, and I'm trying to get Oauth2 for Untappd working. At the moment however, I don't seem to send the correct values to Untappd, as I'm getting indications that I'm not sending the required params in the URL.

    I have the following Grant configuration:

        untappd: {
          authorize_url: "https://untappd.com/oauth/authenticate",
          access_url: "https://untappd.com/oauth/authorize",
          oauth: 2,
          key: "",
          secret: "",
          callback: `${strapi.config.server.url}/auth/untappd/callback`,
    

    I am getting back the 'code' parameter to the callback URL, but after that it doesn't work. Untappd requires a GET request to get the access_token back with the following parameters:

    https://untappd.com/oauth/authorize/?client_id=CLIENTID&client_secret=CLIENTSECRET&response_type=code&redirect_url=REDIRECT_URL&code=CODE
    

    However, I get the message from Untappd:

    http_code = 500. Param_error: Missing either the client_id, redirect_url, client_secret or code parameter. Please check your request and try again
    

    I already tried to use the token_endpoint_auth_method: "client_secret_basic", as to my understanding Grant is using a POST request to get the access_token, right? But adding this option to the config this doesn't change anything.

    Does anyone have a clue what to do?

    opened by adriaanh 18
  • OAuth 1, wrong OAuth redirect

    OAuth 1, wrong OAuth redirect

    Hi !

    I'm trying to use grant with Sellsy (https://go.sellsy.com/fr/).

    Here is my configuration:

    {
        sellsy: {
          enabled: true,
          request_url: "https://apifeed.sellsy.com/0/request_token",
          authorize_url: "https://apifeed.sellsy.com/0/oauth/access_token",
          access_url: "https://apifeed.sellsy.com/0/login.php?",
          oauth: 1,
          key: "XXX",
          secret: "XXX",
          callback: "http://localhost:1337/auth/sellsy/callback",
        }
    }
    

    The problem is that the url where grant redirects the user is: http://localhost:1337/connect/authentification_url=https://apifeed.sellsy.com/0/login.php&oauth_token=0a1bfe71494c3fe920c2adfd9bb5853dc2d1f&oauth_token_secret=395dsqfqfdff8daae8de6da5879ed053&oauth_callback_confirmed=true

    It doesn't work but if I remove the first part ('http://localhost:1337/connect/authentification_url=') and if I replace the '&' after login.php with an '?' the redirection works.

    What am I missing with the configuration?

    Thanks a lot

    opened by rmonnier9 17
  • Grant and ReverseProxy using an extra path element

    Grant and ReverseProxy using an extra path element

    I have much success combining google and FB oauth into a single node script over ReverseProxy! 😁 However, when I attempt to insert another "pathname" into the proxy, the callback fails. in other words, when I am using https://domain.com/GRANT/ everything appears to work just fine in the proxy, both google and FB allow me to successfully log in.

    But in the callback, I get back this: The requested URL /oauth/connect/google/callback was not found on this server.
    Somehow the /GRANT/ pathname is getting lost, both on FB and Google, since it should be "/GRANT/connect/google/callback". is there a config setting I am missing? I have tried using the "path" element as well, but when I try setting that, I am no longer able to even access either FB or google, i get back "404 page not found"

        "oauth": {
            "redirect" : "/",
             "defaults" : {
                    "protocol" : "https",
                    "host" : "featherstest.website/feathers",
                    "path" : "/feathers"    /* this seems to cause more problems for me */
            },
          "facebook": {
            "key": "5XXXXXXXXXXXXXXXX4",
            "secret": "7XXXXXXXXXXXX8a",
            "scope": ["public_profile, email"]
          },
          "google": {
            "key": "81XXXXXXXXXXXXXdi.apps.googleusercontent.com",
            "secret": "XI2XXXXXXXXXXXXXXXMi",
            "scope": [
              "email",
              "profile",
              "openid"
            ]
          }
        }
    

    any suggestions?

    opened by edwardsmarkf 16
  • Fastify integration

    Fastify integration

    Fastify has almost 11,000 stars on github, over 50,000 weekly downloads, is depended upon by 240 packages and claims to be very fast.

    I'd like to see it supported, hopefully pitch in.

    Perhaps we could envisage splitting up the modules (grant and grant-profile) according consumers, but that would be a breaking change, and topic for another issue. Let me know if I should open that too.

    opened by millette 16
  • Mailchimp as provider

    Mailchimp as provider

    As anyone successfully made grant work for Mailchimp as the provider? I have tried everything possible but all I get is the code, it doesn't make the final call to get the access_token

    opened by parkerproject 15
  • "response.raw" not containing extra parameters

    Using this configuration:

    var connectConfig = {
                "server": {
                    "protocol": "http",
                    "host": "localhost:3000",
                    "transport": "session",
                },
                "intuit": {
                    "request_url": "https://oauth.intuit.com/oauth/v1/get_request_token",
                    "authorize_url": "https://appcenter.intuit.com/Connect/Begin",
                    "access_url": "https://oauth.intuit.com/oauth/v1/get_access_token",
                    "oauth": 1,
                    "custom_parameters": ["realmId", "dataSource"],
                    "key": "MY_KEY",
                    "secret": "MY_SECRET",
                    "callback": "/oauth/connect/intuit"
                }
            }
            ;
    app.use(new Grant(connectConfig));
    

    When I print console.log(req.session.grant.response.raw); I get:

    { oauth_token_secret: 'THE_TOKEN_SECRET',
      oauth_token: 'THE_TOKEN' }
    

    Even though I do see a request to the default callback, with those params: http://localhost:3000/connect/intuit/callback?oauth_token=THE_TOKEN&oauth_verifier=VERIFIER&realmId=A_CORRECT_REALM_ID&dataSource=QBO

    I'm guessing I'm just doing something wrong here... Any help would be greatly appreciated :)

    opened by noyfactor 15
  • Set state by default?

    Set state by default?

    Now that I think of it, we should just generated random state by default, right? This is the secure approach and grant is already using session (which it might as well use to store state).

    Any reason for us to do a dynamic overwrite manually?

    opened by bitinn 13
  • Keycloak resource claim are not mapped

    Keycloak resource claim are not mapped

    @simov I am trying to integrate Directus with Keycloak. Following is the JWT decode returned by the Keycloak

    {
      "exp": 1627481458,
      "iat": 1627481157,
      "auth_time": 1627481157,
      "jti": "17b94735-025f-4e87-a74e-83c315d1827f",
      "iss": "http://localhost:9090/auth/realms/Adapt",
      "aud": "account",
      "sub": "09a3ceb7-fb2e-4983-9f19-cd6b236e050e",
      "typ": "Bearer",
      "azp": "directus-ui",
      "session_state": "fd0d6f25-e7ca-4640-bc23-0d7df8ab1f9e",
      "acr": "1",
      "allowed-origins": [
        "http://localhost:8050",
        "http://localhost:8055"
      ],
      "realm_access": {
        "roles": [
          "default-roles-adapt",
          "offline_access",
          "system_admin",
          "uma_authorization"
        ]
      },
      "resource_access": {
        "account": {
          "roles": [
            "manage-account",
            "manage-account-links",
            "view-profile"
          ]
        }
      },
      "scope": "openid email profile",
      "tenant_id": "system",
      "email_verified": false,
      "name": "admin adapt",
      "preferred_username": "system~~admin",
      "given_name": "admin",
      "family_name": "adapt",
      "email": "[email protected]"
    }
    

    Login works correctly. The only thing missing is Authorization, I mean I cannot find Keycloak roles in the Grantjs decoded token. Config used are these:

    OAUTH_KEYCLOAK_SCOPE="openid email"
    
    const config: any = {
    	defaults: {
    		origin: env.PUBLIC_URL,
    		transport: 'session',
    		prefix: '/auth/oauth',
    		response: ['tokens', 'profile','jwt'],
    	},
    };
    
          response: {
            id_token: "xxxxxx",
            access_token: "xxxxxxx",
            refresh_token: "xxxxxxx",
            jwt: {
              id_token: {
                header: {
                  alg: "RS256",
                  typ: "JWT",
                  kid: "IbC514Ciid6SJpPA6_JadZoTFsNEYp8pjOAJ4OCpeRs",
                },
                payload: {
                  exp: 1627483234,
                  iat: 1627482934,
                  auth_time: 1627482934,
                  jti: "d0c6d11f-f857-42c6-99e2-19bc08197fcb",
                  iss: "http://localhost:9090/auth/realms/Adapt",
                  aud: "directus-ui",
                  sub: "09a3ceb7-fb2e-4983-9f19-cd6b236e050e",
                  typ: "ID",
                  azp: "directus-ui",
                  session_state: "a28f0288-f389-485e-a4d6-32d1436a1bd6",
                  at_hash: "b-_q7piDnSsUtwbT9jypMA",
                  acr: "1",
                  tenant_id: "system",
                  email_verified: false,
                  name: "admin adapt",
                  preferred_username: "system~~admin",
                  given_name: "admin",
                  family_name: "adapt",
                  email: "[email protected]",
                },
                signature: "xxxxxxx",
              },
            },
            profile: {
              tenant_id: "system",
              sub: "09a3ceb7-fb2e-4983-9f19-cd6b236e050e",
              email_verified: false,
              name: "admin adapt",
              preferred_username: "system~~admin",
              given_name: "admin",
              family_name: "adapt",
              email: "[email protected]",
            },
          },
        },
      },
    '
    

    Please help.

    opened by phxgbl 11
  • Fix Twitter

    Fix Twitter

    master is throwing this:

    {
      output: {
        access_token: 'redacted-redacted',
        access_secret: 'redacted'
      }
    }
    error - TypeError: Cannot read properties of undefined (reading 'user_id')
        at Object.twitter (foo/node_modules/grant/lib/profile.js:104:39)
        at foo/node_modules/grant/lib/profile.js:40:49
        at processTicksAndRejections (node:internal/process/task_queues:96:5)
    
    opened by KishanBagaria 0
  • Is there a framework-agnostic API?

    Is there a framework-agnostic API?

    I'm very interested in using grant, but I'm not using any of the frameworks (express, koa, etc.) listed in the docs - I'm using solid-start which is relatively new, and which uses file-based routing for defining API routes. I'm assuming I would have to roll my own wrappers which call grant - is there a way I can do this?

    opened by viridia 1
  • OAuth 2 state - how can I associate a successful grant with a user id

    OAuth 2 state - how can I associate a successful grant with a user id

    Greetings, and thank you for your work on this package.

    My use case involves allowing users of my website to allow us to access various services of theirs (e.g. google/gmail to send an email on their behalf). I have been utilizing my own hacked solution until now, and I was passing the "userId" of the currently logged in user as the OAuth2 "state" parameter. Upon receipt of the granted tokens I'd grab the userId off the state and make the association between the grant and the user for my DB.

    I'm using next-js and hence am utilizing the vercel handler. I'm not seeing an obvious mechanism to do this - would you mind providing some clues.

    Also, given that the vercel handler seems to use a single store/Session(), is there an issue with overlapping oauth requests between multiple users?

    Thank you in advance for any insights

    opened by fuzing 5
  • Do you know an example of Grant setup in SvelteKit project?

    Do you know an example of Grant setup in SvelteKit project?

    Hi,

    I want to configure an OAuth 2 authentication connected to self-hosted GitLab instance for a SvelteKit SSR project.

    Here is my personnel exploration issue.

    I try to use Grant library instead node-oauth or passport-oauth2

    Question: do you know an example of Grant setup in SvelteKit project?

    I intend to try to setup Grand in a SvelteKit hooks, based on https://github.com/simov/grant/blob/master/lib/handler/vercel.js source code :thinking:

    Best regards,
    Stéphane

    opened by stephane-klein 3
Owner
simo
(λ (λ (λ (λ (λ (⌐■_■))))))
simo
The official proxy of Titanium Network with enhanced support for a large majority of sites with hCAPTCHA support. Successor to Alloy Proxy.

Corrosion Titanium Networks main web proxy. Successor to Alloy Installation: npm i corrosion Example: const Corrosion = require('corrosion'); const p

Titanium Network 79 Dec 21, 2022
proxy 🦄 yxorp is your Web Proxy as a Service (SAAS) Multi-tenant, Multi-Threaded, with Cache & Article Spinner

proxy ?? yxorp is your Web Proxy as a Service (SAAS) Multi-tenant, Multi-Threaded, with Cache & Article Spinner. Batteries are included, Content Spinning and Caching Engine, all housed within a stunning web GUI. A unique high-performance, plug-and-play, multi-threaded website mirror and article spinner

4D/ҵ.com Dashboards 13 Dec 30, 2022
Proxy but misspelled -- closed proxy for the internet

pyrox Proxy that runs on Cloudflare Workers. Setup Install wrangler2. npm install wrangler. Generate a public Ed25519 key, exported under SPKI mode wi

bots.gg 10 Sep 9, 2022
JavaScript OAuth 1.0a signature generator (RFC 5849) for node and the browser

OAuth 1.0a signature generator for node and the browser Compliant with RFC 5843 + Errata ID 2550 and community spec Installation Install with npm: npm

Marco Bettiolo 230 Dec 16, 2022
Remix Auth plugin for Twitter OAuth 1.0a

Remix Auth Twitter Remix Auth plugin for Twitter OAuth 1.0a. Supported runtimes Runtime Has Support Node.js ✅ Cloudflare ✅ Demo Try out live demo (sou

na2hiro 13 Dec 31, 2022
Express.js middleware implementation for Twitter OAuth 2.0 Client.

twitter-oauth2 Express.js middleware implementation for Twitter OAuth 2.0 Client. This module supports the following grant type available on twitter:

56 19 Dec 17, 2022
OAuth 2 / OpenID Connect Client for Web API runtimes

OAuth 2 / OpenID Connect Client for Web APIs runtime This is a collection of bits and pieces upon which a more streamlined Client module may be writte

Filip Skokan 187 Jan 6, 2023
Express middleware for easy OAuth with a variety of providers.

accounted4 Express middleware for easy OAuth2 with a variety of providers. accounted4 is intended to make it easy for developers to add third-party OA

Josh Moore 3 May 7, 2022
📋 Todo List CRUD and OAuth with Firebase

Todo List CRUD and OAuth with Firebase Esta es una app hecha con React y Firebase en la que puedas crear, leer, actualizar y borrar tareas dentro de u

Adonys Santos 4 May 28, 2022
Minimalistic pre-configured OAuth 2.0 client for Deno. Inspired by grant.

DenoGrant Minimalistic pre-configured OAuth 2.0 client for Deno. Inspired by Grant. NOTE: this is alpha software subject to breaking changes at anytim

CJ R. 12 Dec 13, 2022
A full-featured http proxy for node.js

node-http-proxy node-http-proxy is an HTTP programmable proxying library that supports websockets. It is suitable for implementing components such as

http ... PARTY! 13.1k Jan 3, 2023
Full-featured, middleware-oriented, programmatic HTTP and WebSocket proxy for node.js

rocky A multipurpose, full-featured, middleware-oriented and hackable HTTP/S and WebSocket proxy with powerful built-in features such as versatile rou

Tom 370 Nov 24, 2022
Global HTTP/HTTPS proxy agent configurable using environment variables.

global-agent Global HTTP/HTTPS proxy configurable using environment variables. Usage Setup proxy using global-agent/bootstrap Setup proxy using bootst

Gajus Kuizinas 267 Dec 20, 2022
📦🔐A lightweight private proxy registry build in Node.js

Version 6 (Development branch) Looking for Verdaccio 5? Check branch 5.x. Verdaccio is a simple, zero-config-required local private npm registry. No n

Verdaccio 14.3k Dec 31, 2022
A simple url shorter API built with nodejs running on Kubernetes in Google Cloud, using PostgreSQL for storage and cloud sql proxy.

Simple URL Shorter - Google Cloud - Kubernetes A simple url shorter API built with nodejs running on Kubernetes in Google Cloud, using PostgreSQL for

null 3 Nov 25, 2021
A proxy web app that serves ABC iView content outside of the iView webplayer, avoiding intrusive data harvesting.

iview-proxy A proxy web app that serves ABC iView content outside of the iView webplayer, avoiding intrusive data harvesting. There's also a cool Andr

The OpenGov Australia Project 11 Jul 16, 2022
基于 gh-proxy + Jsdelivr+ cnpmjs + cloudflare workers 的 GitHub Serverless API 工具。

better-github-api Better, Eazy, Access Anywhere 介绍 基于 gh-proxy + Jsdelivr + cnpmjs + cloudflare workers 的 GitHub Serverless API 工具。 cdn.js:仅含 gh-proxy

One Studio 11 Nov 23, 2022
一个可以将 Clash 订阅转换成 Proxy Provider 和 External Group(Surge) 的工具

Proxy Provider Converter 一个可以将 Clash 订阅转换成 Proxy Provider 和 External Group(Surge) 的工具 https://proxy-provider-converter.vercel.app 什么是 Proxy Provider 和

null 180 Jan 2, 2023
DSC-AlarmServer - Creates web interface to DSC/Envisalink with proxy.

DSC-AlarmServer Creates web interface to DSC/Envisalink with proxy. Since the Envisalink module can only have one connection, this phython script can

null 4 Oct 11, 2022
Simple proxy that is intended to support on chaos testing.

Proxy with Behavior Proxy with Behavior is a node application that work as a reverse proxy, and enables apply some behaviors to be executed in request

José Carlos de Moraes Filho 7 Jan 28, 2022