Top Related Projects
OAuth wrapper for node.js
Simple, unobtrusive authentication for Node.js.
The most scalable and customizable OpenID Certified™ OpenID Connect and OAuth Provider on the market. Become an OpenID Connect and OAuth2 Provider over night. Broad support for related RFCs. Written in Go, cloud native, headless, API-first. Available as a service on Ory Network and for self-hosters.
OAuth 2 / OpenID Connect Client API for JavaScript Runtimes
Quick Overview
Grant is a comprehensive OAuth middleware for Node.js and Express.js applications. It simplifies the process of integrating various OAuth providers into web applications, supporting over 200 OAuth providers out of the box. Grant handles the complex OAuth flow, making it easier for developers to implement authentication and authorization in their projects.
Pros
- Supports a wide range of OAuth providers (200+)
- Easy to set up and configure
- Flexible and customizable
- Regularly updated and maintained
Cons
- Requires some understanding of OAuth concepts
- Documentation can be overwhelming due to the large number of supported providers
- May introduce additional complexity for simple authentication needs
- Limited to Node.js and Express.js environments
Code Examples
- Basic setup with Express:
const express = require('express')
const grant = require('grant').express()
const app = express()
app.use(grant({
defaults: {
origin: 'http://localhost:3000',
transport: 'session'
},
google: {
key: 'GOOGLE_CLIENT_ID',
secret: 'GOOGLE_CLIENT_SECRET',
scope: ['profile', 'email'],
callback: '/callback'
}
}))
- Handling the callback:
app.get('/callback', (req, res) => {
console.log(req.session.grant.response)
res.end(JSON.stringify(req.session.grant.response, null, 2))
})
- Using with custom configuration:
const config = require('./config.json')
app.use(grant(config))
Getting Started
-
Install Grant:
npm install grant
-
Set up your Express app with Grant middleware:
const express = require('express') const session = require('express-session') const grant = require('grant').express() const app = express() app.use(session({secret: 'grant', saveUninitialized: true, resave: false})) app.use(grant({ defaults: { origin: 'http://localhost:3000', transport: 'session' }, google: { key: 'GOOGLE_CLIENT_ID', secret: 'GOOGLE_CLIENT_SECRET', scope: ['profile', 'email'], callback: '/callback' } })) app.get('/callback', (req, res) => { res.end(JSON.stringify(req.session.grant.response, null, 2)) }) app.listen(3000)
-
Replace
GOOGLE_CLIENT_ID
andGOOGLE_CLIENT_SECRET
with your actual Google OAuth credentials. -
Run your app and navigate to
http://localhost:3000/connect/google
to start the OAuth flow.
Competitor Comparisons
OAuth wrapper for node.js
Pros of node-oauth
- Mature and well-established library with a long history
- Supports both OAuth 1.0a and OAuth 2.0 protocols
- Provides low-level control over OAuth flows
Cons of node-oauth
- Less active development and maintenance compared to grant
- More complex implementation requiring more code for basic OAuth flows
- Limited built-in support for popular OAuth providers
Code Comparison
node-oauth:
const OAuth = require('oauth').OAuth;
const oauth = new OAuth(
'https://api.twitter.com/oauth/request_token',
'https://api.twitter.com/oauth/access_token',
consumerKey,
consumerSecret,
'1.0A',
null,
'HMAC-SHA1'
);
grant:
const grant = require('grant').express()
const config = {
defaults: { protocol: 'http', host: 'localhost:3000' },
twitter: { key: 'consumer_key', secret: 'consumer_secret' }
}
app.use(grant(config))
Summary
While node-oauth offers more granular control and supports both OAuth versions, it requires more complex implementation. Grant, on the other hand, provides a simpler, more modern approach with built-in support for many providers, making it easier to integrate OAuth into applications. Grant's active development and maintenance also give it an edge for current and future projects.
Simple, unobtrusive authentication for Node.js.
Pros of Passport
- More comprehensive authentication solution with support for local strategies
- Larger ecosystem with numerous plugins and integrations
- Well-established and widely adopted in the Node.js community
Cons of Passport
- Steeper learning curve due to its extensive feature set
- Can be overkill for simple authentication needs
- Requires more setup and configuration
Code Comparison
Passport:
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "http://www.example.com/auth/google/callback"
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return done(err, user);
});
}
));
Grant:
const grant = Grant({
defaults: {
protocol: 'http',
host: 'localhost:3000'
},
google: {
key: GOOGLE_CLIENT_ID,
secret: GOOGLE_CLIENT_SECRET,
callback: '/callback',
scope: ['profile', 'email']
}
})
Grant offers a more concise configuration for OAuth providers, while Passport requires more detailed setup but provides greater flexibility for custom authentication logic.
The most scalable and customizable OpenID Certified™ OpenID Connect and OAuth Provider on the market. Become an OpenID Connect and OAuth2 Provider over night. Broad support for related RFCs. Written in Go, cloud native, headless, API-first. Available as a service on Ory Network and for self-hosters.
Pros of Hydra
- More comprehensive OAuth2 and OpenID Connect server implementation
- Supports advanced security features like JSON Web Key rotation
- Designed for high-availability and scalability in enterprise environments
Cons of Hydra
- Steeper learning curve due to its complexity
- Requires more setup and configuration compared to Grant
- May be overkill for simple OAuth implementations
Code Comparison
Grant (OAuth flow initialization):
app.get('/connect/:provider', grant.express({
key: 'CONSUMER_KEY',
secret: 'CONSUMER_SECRET',
scope: ['email', 'profile']
}))
Hydra (OAuth2 consent flow):
challenge, err := hydra.GetConsentRequest(r.URL.Query().Get("consent_challenge"))
// ... handle consent
acceptConsentRequest := hydra.AcceptConsentRequest{
GrantScope: challenge.RequestedScope,
Session: &openid.Session{},
}
Grant focuses on simplifying OAuth flows with a concise API, while Hydra provides a more robust and feature-rich implementation suitable for complex scenarios. Grant is easier to set up for basic use cases, whereas Hydra offers more control and security features at the cost of increased complexity.
OAuth 2 / OpenID Connect Client API for JavaScript Runtimes
Pros of openid-client
- More focused on OpenID Connect and OAuth 2.0 standards compliance
- Provides advanced features like dynamic client registration and token introspection
- Better suited for enterprise-level applications with complex authentication requirements
Cons of openid-client
- Steeper learning curve due to its comprehensive feature set
- Less straightforward for simple social login scenarios
- Requires more configuration and setup compared to Grant
Code Comparison
Grant example:
const grant = Grant({
defaults: {
protocol: 'http',
host: 'localhost:3000'
},
google: {
key: 'GOOGLE_CLIENT_ID',
secret: 'GOOGLE_CLIENT_SECRET',
scope: ['profile', 'email'],
callback: '/callback'
}
})
openid-client example:
const client = new Issuer.Client({
client_id: 'GOOGLE_CLIENT_ID',
client_secret: 'GOOGLE_CLIENT_SECRET',
redirect_uris: ['http://localhost:3000/callback'],
response_types: ['code']
});
const authorizationUrl = client.authorizationUrl({
scope: 'openid email profile'
});
Both libraries facilitate OAuth authentication, but openid-client offers more granular control and adherence to OpenID Connect standards, while Grant provides a simpler, more abstracted approach for quick implementation of social logins across multiple providers.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Grant
OAuth Proxy
200+ Supported Providers / OAuth Playground
23andme
| 500px
| acton
| acuityscheduling
| adobe
| aha
| alchemer
| amazon
| angellist
| apple
| arcgis
| asana
| assembla
| atlassian
| auth0
| authentiq
| authing
| autodesk
| aweber
| axosoft
| baidu
| basecamp
| battlenet
| beatport
| bitbucket
| bitly
| box
| buffer
| campaignmonitor
| cas
| cheddar
| clio
| cognito
| coinbase
| concur
| constantcontact
| coursera
| crossid
| 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
| flickr
| 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
| notion
| nylas
| okta
| onelogin
| openstreetmap
| optimizely
| osu
| patreon
| paypal
| phantauth
| pinterest
| plurk
| podio
| procore
| producthunt
| projectplace
| pushbullet
| qq
| ravelry
| redbooth
| reddit
| runkeeper
| salesforce
| sellsy
| shoeboxed
| shopify
| skyrock
| slack
| slice
| smartsheet
| smugmug
| snapchat
| snowflake
| socialpilot
| socrata
| soundcloud
| spotify
| square
| stackexchange
| stocktwits
| stormz
| storyblok
| strava
| stripe
| surveymonkey
| surveysparrow
| thingiverse
| ticketbud
| tiktok
| timelyapp
| todoist
| trakt
| traxo
| trello
| tripit
| trustpilot
| tumblr
| twitch
| twitter
| typeform
| uber
| unbounce
| underarmour
| unsplash
| untappd
| upwork
| uservoice
| vend
| venmo
| vercel
| verticalresponse
| viadeo
| vimeo
| visualstudio
| vk
| wechat
| weekdone
| weibo
| withings
| wordpress
| workos
| wrike
| xero
| xing
| yahoo
| yammer
| yandex
| zendesk
| zoom
Table of Contents
Migration Guide: from v4 to v5
- Providers
- Handlers
- Express / Koa / Hapi / Fastify
- AWS Lambda / Azure Function / Google Cloud Function / Vercel
- Configuration
- Basics / Description / Values / Scopes
- Connect
- Callback
- Dynamic Configuration
- Instance / State / HTTP / OAuth Proxy
- Misc
- Configuration / Handlers / Request / Types / OAuth Quirks
- Examples
- Changelog
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
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
- origin - where your client server can be reached
- provider - any supported provider
google
|twitter
...- key -
consumer_key
orclient_id
of your OAuth app - secret -
consumer_secret
orclient_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
...
- key -
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:
-
The first place Grant looks for configuration is the built-in oauth.json file located in the config folder.
-
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. -
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 adefaults
configuration is only a convenience. You can define all available options directly for a provider. -
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. -
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.
-
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:
aud
- is the token intended for my OAuth app?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_reposcope
/connect/github/notifications
to request the notificationsscope
using another OAuth App (key
andsecret
)/connect/github/all
to request a bunch ofscope
s and also receive the response data in anothercallback
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 by using 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 potentially may 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 scope
s 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 a 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": "my_email@gmail.com",
"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"
}
Openstreetmap
Openstreetmap OAuth 2.0 applications have to use the openstreetmap2
provider:
"openstreetmap2": {
"state": true,
"scope": [
"openid",
"read_prefs"
]
}
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"}
}
Twitter OAuth 1.0a custom scope parameter can be specified in two ways:
"twitter": {
"custom_params": {"x_auth_access_type": "read"}
}
"twitter": {
"scope": ["read"]
}
Twitter OAuth 2.0 applications have to use the twitter2
provider:
"twitter2": {
"state": true,
"pkce": true,
"scope": [
"users.read",
"tweet.read"
]
}
VisualStudio
Set your Client Secret as secret
not the App Secret:
"visualstudio": {
"key": "APP_ID",
"secret": "CLIENT_SECRET instead of APP_SECRET"
}
Top Related Projects
OAuth wrapper for node.js
Simple, unobtrusive authentication for Node.js.
The most scalable and customizable OpenID Certified™ OpenID Connect and OAuth Provider on the market. Become an OpenID Connect and OAuth2 Provider over night. Broad support for related RFCs. Written in Go, cloud native, headless, API-first. Available as a service on Ory Network and for self-hosters.
OAuth 2 / OpenID Connect Client API for JavaScript Runtimes
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot