How to use OAuth with Express in NodeJS
Often in apps, we need to pull certain information from other services. Consider a scenario where you need to build a web application to assist hiring and recruitment. In such scenario, we often need to pull data from other sources like (Github, Linkedin, Stack Overflow etc). One of the best way to handle that transfer of information without compromising on the security is via OAuth.
The basic steps involved in Oauth are :-
- Register an application for the service you intend to use within their website. In this example, we would be using Github as an example. Give an appropriate app name and provide the Authorization callback URL as
http://localhost:3001/connect/github/callback
. We will get back to this later. - Users from your app are redirected to request their GitHub identity.
- Users are redirected back to your site by GitHub with the access token.
- Your app accesses the API with the user's access token.
So, lets start building this in Node and the module we would be using for this is grant
.
npm install grant-express --save
-
grant
needs to have its config file for Oauth. So, lets create a config fileWe need 4 parameters for this config file :-
- Protocol: use
http
for local development - Host: The server host (
http://localhost:3001
). - Github key: You will get this when you register for an app in Github. If you didn't note down, you can still visit here and check/regenerate it.
- Github secret: You will get this also when you register your app.
callback
is the most important parameter here as that's the route which gets invoked after the user is authenticated by Github. Provide the callback url as/auth/handle_github_callback
.-
scope
is another important parameter which is used to request the scopes which app need to access information. You can see the list of scopes here.Do use
config
module to store all your config parameters across different environmentsvar config = require('config') module.exports = (github) => { return { server: { protocol: config.get('protocol'), host: config.get('host') }, github: { key: github.key, secret: github.secret, callback: '/auth/handle_github_callback', scope: [] } } }
- Protocol: use
-
Now, lets create the callback routes. This is the value we had specified within the config file as
callback
.var express = require('express') var router = express.Router() var config = require('config') router.get('/handle_github_callback', function (req, res) { const {error, error_description, error_uri} = req.query if (error) { res.status(500).json({ error, error_description, error_uri }) } else { res.cookie('access-token-github', req.query.access_token) res.redirect(config.get('callback_url')) } }) module.exports = router
So, we can see that once the user is authenticated, we get the access token as one of the request query parameters. Usually within Oauth, the providers send in a parameter named as '
expires_in
field which indicates the time within which the token will expire. Surprisingly, for Github, it doesnt return this value. There are some important things you should keep in mind while dealing with access tokens.- Ideally you should use a low expiration time, then renew. This minimizes the window for a malicious user to use a stolen JWT (you can revoke refresh tokens, which will then prevent your application from obtaining new id_tokens).
- Some providers allow us to specify the expiration time. Grant allows us to configure that via custom parameters. Read the documentation here.
- You need to check on the storage of these access tokens. For this example, i am sending the access token as a cookie. You need to consider the security aspects while storing in cookies. Storing in cookies are vulnerable to CSRF attacks.
-
Finally the main piece connecting everything is to have the grant defined in your server app file (
app.js
). For the development environment, i store the key and secret within my node environment variables. I pass on those values to the function which creates my Grant config file and which is then injected into the app.const {GITHUB_KEY, GITHUB_SECRET} = process.env const github = { key: GITHUB_KEY, secret: GITHUB_SECRET } var grant = new Grant(createConfig(github)) app.use(grant) app.use('/auth', authCallback)
The above code does 2 important things :-
- The server is configured to hit the provider (Github) when you access via
http://localhost:3001/connect/github
. This would pop up the authentication screen as below. - Once you approve it, it will be first redirected to
http://localhost:3001/connect/github/callback
(this is what was specified when we were registering our app with Github as thats the first route which will be triggered by Grant) which is then finally redirected to our callback routes which we had defined in our config file (`http://localhost:3001/auth/handle_github_callback). -
Please note that certain routes are reserved for Grant.
/connect/:provider/:override? /connect/:provider/callback
- When our callback route is finally called, we get the
access_token
which is required to trigger certain Github authenticated API's.
- The server is configured to hit the provider (Github) when you access via
-
Try using that
access_token
to hit the routeto fetch email of the user from Github
`https://api.github.com/user?access_token=<access_token_val>`