Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

OAuth Middleware for Express, Koa and Hapi

License

NotificationsYou must be signed in to change notification settings

JavaScriptExpert/grant

 
 

Repository files navigation

npm-versiontravis-cicoveralls-status

180+ Supported Providers /OAuth Playground

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

Table of Contents

Express

npm install grant-express
varexpress=require('express')varsession=require('express-session')vargrant=require('grant-express')varapp=express()// REQUIRED: any session store - see /examples/express-session-storesapp.use(session({secret:'grant'}))// mount grantapp.use(grant({/*configuration - see below*/}))

Koa

npm install grant-koa
varKoa=require('koa')varsession=require('koa-session')varmount=require('koa-mount')vargrant=require('grant-koa')varapp=newKoa()// REQUIRED: any session store - see /examples/koa-session-storesapp.keys=['grant']app.use(session(app))// mount grantapp.use(mount(grant({/*configuration - see below*/})))

Hapi

npm install grant-hapi
varHapi=require('hapi')varyar=require('yar')vargrant=require('grant-hapi')varserver=newHapi.Server()server.register([// REQUIRED: any session store - see /examples/hapi-session-stores{plugin:yar,options:{cookieOptions:{password:'grant',isSecure:false}}},// mount grant{plugin:grant(),options:{/*configuration - see below*/}}])

Configuration

{"defaults": {"protocol":"http","host":"localhost:3000","callback":"/callback","transport":"session","state":true  },"google": {"key":"...","secret":"...","scope": ["scope1","scope2"],"nonce":true,"custom_params": {"access_type":"offline"},"callback":"/google/callback"  },"twitter": {"key":"...","secret":"..."  }}
  • defaults - default configuration for all providers(previously this option was calledserver)
    • protocol - eitherhttp orhttps
    • host - your server's host namelocalhost:3000 |dummy.com:5000 |mysite.com ...
    • path -path prefix to use for the Grant middleware(defaults to empty string if omitted)
    • callback - common callback route for all providers in your config/callback |/done ...
    • transport - transport to use to deliver theresponse data in your final callback routequerystring |session(defaults to querystring if omitted)
    • state - generate random state string on each authorization attempttrue |false(OAuth2 only, defaults to false if omitted)
  • provider - anysupported providerfacebook |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
    • callback - specific callback route to use for this provider only/callback |/done ...
    • custom_params - customauthorization parameters
    • nonce - generate random nonce string on each authorization attempttrue |false(OpenID Connect only, defaults to false if omitted)
    • dynamic - list of options that can beoverridden dynamically['subdomain', 'scope']

Each provider can override any of the default options. Thedefaults key is completely optional.

Additionally every provider can override any of thereserved keys, some of which arepredefined internally.

Redirect URL

Forredirect URL of your OAuth application you shouldalways use this format:

[protocol]://[host]/connect/[provider]/callback

Whereprotocol andhost should match the ones from which you initiate the OAuth flow, andprovider is the provider's name from the list ofsupported providers.

Thisredirect URL is used internally by Grant. You will receive theresponse data from the OAuth flow inside the route specified in thecallback key of your Grant configuration.

See thePath Prefix section on how to configure the redirect URL when using thepath configuration option.

Reserved Routes

Grant operates on the following routes:

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

Path Prefix

You can mount Grant under specific path prefix:

// Expressapp.use('/path/prefix',grant(config))// Koaapp.use(mount('/path/prefix',grant(config)))// Hapiserver.register([{routes:{prefix:'/path/prefix'},plugin:grant(config)}])

In this case it is required to specify the path prefix using thepath configuration option for thedefaults key:

{"defaults": {"protocol":"...","host":"...","path":"/path/prefix"  }}

Lastly that path prefix should be specified in your OAuth application's redirect URL as well:

[protocol]://[host][path]/connect/[provider]/callback

In case you want your callback routes prefixed, set them accordingly:

{"facebook": {"callback":"/path/prefix/handle_facebook_callback"  }}

OpenID Connect

Thenonce option isrecommended when requesting theopenid scope:

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

Grantdoes not verify the signature of the returnedid_token because that requires discovery, caching, and expiration of the provider's public keys.

However, Grant tries to decode theid_token and verifies the following two claims(returns error respectively):

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

For convenience theresponse data contains the decodedid_token.

Take a look at the OpenID Connectexample.

Custom Parameters

Some providers may employ custom authorization parameters, that you can pass using thecustom_params option:

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

Alternatively any custom parameter that is not areserved key, and is listed under thecustom_parameters array for thatprovider, can be defined along with the rest of the options.

Static Overrides

You can add arbitrary{object} keys inside your provider's configuration to create sub configurations that override theglobal settings for that provider:

// navigate to /connect/facebook"facebook":{"key":"...","secret":"...",// by default request publish permissions"scope":["publish_actions","publish_stream"],// set specific callback route on your server for this provider"callback":"/facebook/callback",// navigate to /connect/facebook/groups"groups":{// request only group permissions"scope":["user_groups","friends_groups"]},// navigate to /connect/facebook/pages"pages":{// request only page permissions"scope":["manage_pages"],// additionally use specific callback route on your server for this override"callback":"/facebook_pages/callback"}}

The custom key names cannot be one of thereserved keys.

Dynamic Override

In some cases you may want to allow the user to override certain parts of your configuration dynamically.

For example forshopify you have to embed the user's shop name into the OAuth URLs, so it makes sense to allow thesubdomain option to be overridden dynamically:

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

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

<formaction="/connect/shopify"method="post"accept-charset="utf-8"><inputtype="text"name="subdomain"value=""/><button>submit</button></form>

Keep in mind that when makingPOST request to the/connect/:provider/:override? route you have to mount thebody-parser middleware for Express and Koa before mounting Grant:

// expressvarparser=require('body-parser')app.use(parser.urlencoded({extended:true}))app.use(grant(config))// koavarparser=require('koa-bodyparser')app.use(parser())app.use(mount(grant(config)))

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

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

Reserved Keys

List of allreserved keys and their typical location:

KeyLocationDescription
request_urloauth.jsonOAuth1/step1
authorize_urloauth.jsonOAuth1/step2 or OAuth2/step1
access_urloauth.jsonOAuth1/step3 or OAuth2/step2
oauthoauth.jsonOAuth version number
scope_delimiteroauth.jsonstring delimiter used for concatenating multiple scopes
custom_parametersoauth.jsonlist of known custom authorization parameters
protocol, host, pathdefaultsused to generateredirect_uri
transportdefaultstransport to use to deliver the response data in your finalcallback route
statedefaultstoggle randomstate string generation for OAuth2
key[provider]OAuth app key, reserved aliases:consumer_key andclient_id
secret[provider]OAuth app secret, reserved aliases:consumer_secret andclient_secret
scope[provider]list of scopes to request
custom_params[provider]custom authorizationparameters and their values
subdomain[provider]string to beembedded inrequest_url,authorize_url andaccess_url
nonce[provider]toggle randomnonce string generation forOpenID Connect providers
callback[provider]final callback route on your server to receive theresponse data
dynamic[provider]allowdynamic override of configuration
namegeneratedprovider'sname, used to generateredirect_uri
[provider]generatedprovider'sname as key
redirect_urigeneratedOAuth appredirect URI, generated usingprotocol,host,path andname
overridesgeneratedall keys containing{object} value are extracted here asstatic overrides

Custom Providers

In case you have a private OAuth provider that you don't want to be part of theofficially supported ones, you can define it in your configuration by adding a custom key for it.

In this case you have to specify all of the required provider keys by yourself:

{"defaults": {"protocol":"https","host":"mywebsite.com"  },"mywebsite": {"authorize_url":"https://mywebsite.com/authorize","access_url":"https://mywebsite.com/token","oauth":2,"key":"[CLIENT_ID]","secret":"[CLIENT_SECRET]","scope": ["read","write"]  }}

Refer to the Grant'sOAuth configuration to see how various providers are configured.

Development Environments

You can easily configure different development environments:

{"development": {"defaults": {"protocol":"http","host":"dummy.com:3000"},"facebook": {"key":"development OAuth app credentials","secret":"development OAuth app credentials"    }  },"staging": {"defaults": {"protocol":"https","host":"staging.mywebsite.com"},"facebook": {"key":"staging OAuth app credentials","secret":"staging OAuth app credentials"    }  },"production": {"defaults": {"protocol":"https","host":"mywebsite.com"},"facebook": {"key":"production OAuth app credentials","secret":"production OAuth app credentials"    }  }}

Then you can pass the environment flag:

NODE_ENV=production node app.js

And use it in your application:

varconfig=require('./config.json')vargrant=Grant(config[process.env.NODE_ENV||'development'])

Subdomain

Some providers have dynamic URLs containing bits of user information embedded in them.

Thesubdomain option can be used to specify your company name, server region or whatever else is required:

"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 entireauthorize_url andaccess_url in your configuration.

Sandbox OAuth URLs

Some providers may havesandbox URLs for testing. To use them just override the entirerequest_url,authorize_url andaccess_url in your configuration(notice thesandbox 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 defaultredirect_uri that Grantgenerates for you.

For example Feedly supports onlyhttp://localhost as redirect URL of their Sandbox OAuth application, and it won't allow thehttp://localhost/connect/feedly/callback path:

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

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

varqs=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 theresponse data in your finalcallback route.

More Dynamic Overrides

In case you really want to, you can allowdynamic overriding of every option for a provider:

{"facebook": {"dynamic":true  }}

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

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

Essentially Grant is anOAuth proxy.

Quirks

Ebay

Set the redirect URL of your OAuth app as usual[protocol]://[host]/connect/ebay/callback. Then Ebay will generate a special string calledRuName (eBay Redirect URL name) that you need to set asredirect_uri in Grant:

"ebay": {"redirect_uri":"[RUNAME]"}

Flickr, Freelancer, Optimizely

Some providers are using custom authorization parameter to pass the requested scopes - Flickrperms, Freelanceradvanced_scopes, Optimizelyscopes, but you can use the regularscope option in your configuration:

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

Mastodon

Mastodon requires the entiredomain of your server to be embedded in the OAuth URLs. However you should use thesubdomain option:

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

SurveyMonkey

Set your Mashery user name askey and your application key asapi_key:

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

Fitbit, LinkedIn, ProjectPlace

Initially these were OAuth1 providers, so thefitbit,linkedin andprojectplace names are used for that. To use their OAuth2 flow append2 at the end of their names:

"fitbit2":{// navigate to /connect/fitbit2},"linkedin2":{// navigate to /connect/linkedin2},"projectplace2":{// navigate to /connect/projectplace2}

VisualStudio

Set your Client Secret assecret not the App Secret:

"visualstudio": {"key":"[APP_ID]","secret":"[CLIENT_SECRET not APP_SECRET]"}

Response Data

The OAuth response data is returned asquerystring in yourfinal callback - the one you specify using thecallback key in your Grant configuration.

Alternatively the response data can be returned inside thesession, see theconfiguration section above and thesession transport example.

OAuth2

Theaccess_token and therefresh_token(if present) are accessible directly. Theid_token(OpenID Connect only) contains the decoded JWT, andraw contains the raw response data:

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

OAuth1

Theaccess_token and theaccess_secret are accessible directly,raw contains the raw response data:

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

Error

In case of an error, theerror key will be populated with the raw error data:

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

Alternative Require

Alternatively you can require any of the middlewares directly fromgrant(each pair is identical):

// ExpressvarGrant=require('grant-express')varGrant=require('grant').express()// KoavarGrant=require('grant-koa')varGrant=require('grant').koa()// HapivarGrant=require('grant-hapi')varGrant=require('grant').hapi()

Alternative Instantiation

Grant can be instantiated with or without using thenew keyword:

varGrant=require('grant-express|koa|hapi')vargrant=Grant(config)// identical to:vargrant=newGrant(config)

Additionally Hapi can accept the configuration in two different ways:

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

Programmatic Access

Every Grant instance have aconfig property attached to it:

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

It contains thegenerated configuration data that Grant uses internally.

You can use theconfig property to alter the Grant's behavior during runtime.

Keep in mind that this affects the entire Grant instance!Dynamic override might be more appropriate.

Session vs Querystring

Grant returns theresult of the OAuth flow in the finalcallback route on your server:

{"facebook": {"callback":"/finally"  }}

By default Grant encodes the result as querystring:/finally?access_token=...

This potentially may leak private data in your server logs, especially if you are behind reverse proxy.

It isrecommended to use the session transport instead:

{"defaults": {"transport":"session"  },"facebook": {"callback":"/finally"  }}

This way the result will no longer be encoded in the querystring:/finally

And you will receive the response data inside thesession instead.

Redirect URI

Theprotocol, thehost (and optionally thepath) options are used to generatethe correctredirect_uri for each provider:

{"defaults": {"protocol":"https","host":"website.com"  },"facebook": {},"twitter": {}}

The above configuration is identical to:

{"facebook": {"redirect_uri":"https://website.com/connect/facebook/callback"  },"twitter": {"redirect_uri":"https://website.com/connect/twitter/callback"  }}

Note thatredirect_uri would overrideprotocol andhost even if they were specified.

Meta Configuration

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

{"google": {"app":"My Awesome OAuth App","owner":"my_email@gmail.com","url":"https://url/to/manage/oauth/app"  }}

These custom keys cannot bereserved ones, and cannot contain{object} value.

Get User Profile

Once you have your access tokens secured, you can start making authorized requests on behalf of your users.

For example, you may want to get the user's profile after the OAuth flow has completed:

varexpress=require('express')varsession=require('express-session')vargrant=require('grant-express')varrequest=require('request-compose').clientvarconfig={"defaults":{"protocol":"http","host":"localhost:3000"},"facebook":{"key":"[APP_ID]","secret":"[APP_SECRET]","callback":"/facebook_callback"}}express().use(session({secret:'grant',saveUninitialized:true,resave:true})).use(grant(config)).get('/facebook_callback',async(req,res)=>{var{body}=awaitrequest({url:'https://graph.facebook.com/me',headers:{authorization:`Bearer${req.query.access_token}`}})res.end(JSON.stringify({oauth:req.query,profile:body},null,2))}).listen(3000)

About

OAuth Middleware for Express, Koa and Hapi

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript99.4%
  • Shell0.6%

[8]ページ先頭

©2009-2025 Movatter.jp