{ Soham Kamani }

About Blog Github Twitter

🌙
☀️

Implementing OAuth 2.0 with Node.js

Updated on

OAuth2 is an authentication protocol that is used to authenticate and authorize users in an application by using another service provider.

This post will go through how to build a Node.js application to implement the OAuth2 protocol.

banner

If you just want to see the code, you can view it here

How OAuth2 Works

Let’s take a brief look at the OAuth protocol before we jump into implementation.

If you’ve ever seen a dialog like this, then you’ve probably used OAuth before:

gitlab using github oauth

Here, we are trying to login to Gitlab using Github to authenticate.

There are three parties in any OAuth mechanism:

  1. The client - The person, or user who is trying to log in
  2. The consumer - The application that the client wants to log into (which is Gitlab in this example)
  3. The service provider - The external application that authenticates the users identity. (which is Github in this example)

In this post, we’ll create a Node.js HTTP server (consumer) that uses Github’s OAuth2 API (service provider) to authenticate the user (client).

Let’s look at an overview of how this would work in practice.

oauth flow diagram

Let’s go into the details of how to implement each part:

Creating the Clients Landing Page

Lets create the first part of the application, which is the landing page. This will be a basic HTML page, with a link that the user can click on to authenticate with Github.

We can create a new file, public/index.html:

<!DOCTYPE html>
<html>

<body>
  <a href="https://github.com/login/oauth/authorize?client_id=myclientid123&redirect_uri=http://localhost:8080/oauth/redirect">
    Login with github
  </a>
</body>

</html>

The link URL has three key parts:

  • https//github.com/login/oauth/authorize is the OAuth gateway for Github’s OAuth flow. All OAuth providers have a gateway URL that you have to send the user to in order to proceed.

  • client_id=myclientid123 - this specifies the client ID of the application. This ID will tell Github about the identity of the consumer who is trying to use their OAuth service.

    OAuth service providers have portal in which you can register your consumer. On registration, you will receive a client ID (which we are using here as myclientid123), and a client secret (which we will use later on). For Github, the portal to register new applications can be found on https://github.com/settings/applications/new. After

  • redirect_uri=http://localhost:8080/oauth/redirect - specifies the URL to redirect to with the request token, once the user has been authenticated by the service provider. Normally, you will have to set this value on the registration portal as well, to prevent anyone from setting malicious callback URLs.

Next, we need to create the HTTP server to serve the index.html file we just made.

The following code would go into a new file index.js:

// Import the express lirbary
const express = require('express')

// Create a new express application and use
// the express static middleware, to serve all files
// inside the public directory
const app = express()
app.use(express.static(__dirname + '/public'))

// Start the server on port 8080
app.listen(8080)

We’ll use the express library to implement the server in this example

You can start the server by executing node index.js and visit http://localhost:8080: You will see the landing page we just made.

Once you click on the “Login with github” link, you will be redirected to the familiar OAuth page to register with Github. However, once you authenticate, you will be redirected to http://localhost:8080/oauth/redirect, which will lead to a 404 page on the server.

Let’s implement it now:

Implementing the Redirect Route

Once the user authenticates with Github, they get redirected to the redirect URL that was specified earlier.

The service provider also adds a request token along with the url. In this case, Github adds this as the code parameter, so the redirect URL will actually be something like http://localhost:8080/oauth/redirect?code=mycode123, where mycode123 is the request token.

We need this request token and our client secret to get the access token, which is the token that is actually used to get information about the user. We get this access token by making a POST HTTP call to https://github.com/login/oauth/access_token along with the mentioned information.

You can view the documentation page for the details of the information Github provides to the redirect URL, and the information we need for provide with the POST /login/oauth/access_token HTTP call.

Let’s add the /oauth/redirect route to the index.js file:

const express = require('express')

// Import the axios library, to make HTTP requests
const axios = require('axios')

// This is the client ID and client secret that you obtained
// while registering the application
const clientID = '<your client id>'
const clientSecret = '<your client secret>'

const app = express()

// Declare the redirect route
app.get('/oauth/redirect', (req, res) => {
  // The req.query object has the query params that
  // were sent to this route. We want the `code` param
  const requestToken = req.query.code
  axios({
    // make a POST request
    method: 'post',
    // to the Github authentication API, with the client ID, client secret
    // and request token
    url: `https://github.com/login/oauth/access_token?client_id=${clientID}&client_secret=${clientSecret}&code=${requestToken}`,
    // Set the content type header, so that we get the response in JSOn
    headers: {
         accept: 'application/json'
    }
  }).then((response) => {
    // Once we get the response, extract the access token from
    // the response body
    const accessToken = response.data.access_token
    // redirect the user to the welcome page, along with the access token
    res.redirect(`/welcome.html?access_token=${accessToken}`)
  })
})

app.use(express.static(__dirname + '/public'))
app.listen(8080)

Now the redirect URL is functional, and will redirect the user to the welcome page, along with the access token.

Redirecting to the Clients Welcome Page

The welcome page is the page we show the user after they have logged in. Now that we have the users access token, we can obtain their account information on their behalf as authorized Github users.

For a list of all APIs available, you can see the Github API Documentation

We will be using the /user API to get basic info about the user and say hi to them on our welcome page. Create a new file public/welcome.html:

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>Hello</title>
</head>

<body>

</body>
<script>
	// We can get the token from the "access_token" query
	// param, available in the browsers "location" global
	const query = window.location.search.substring(1)
	const token = query.split('access_token=')[1]

	// Call the user info API using the fetch browser library
	fetch('https://api.github.com/user', {
			headers: {
				// This header informs the Github API about the API version
				Accept: "application/vnd.github.v3+json",
				// Include the token in the Authorization header
				Authorization: 'token ' + token
			}
		})
		// Parse the response as JSON
		.then(res => res.json())
		.then(res => {
			// Once we get the response (which has many fields)
			// Documented here: https://developer.github.com/v3/users/#get-the-authenticated-user
			// Write "Welcome <user name>" to the documents body
			const nameNode = document.createTextNode(`Welcome, ${res.name}`)
			document.body.appendChild(nameNode)
		})
</script>

</html>

With the addition of the welcome page, our OAuth implementation is now complete!

Once the app starts, we can visit http://localhost:8080/ , authorize with Github, and end up on the welcome page, which displays the greeting. My name on my github profile is “Soham Kamani”, so the weclome page will display Welcome, Soham Kamani once I login.

The source code, and instructions on how to run it can be found here

Additional Security Measures

Although this post demonstrated the basics of OAuth2, there is a lot more that can be done to further secure your application.

Session Tokens

In this example, we passed the access token to the client so that it can make requests as the authorized user. To make your app more secure, the access token should not be passed directly to the user. Instead, create a session token that is sent to the user as a cookie.

The app will maintain a mapping of the session tokens to access tokens on the server side (most likely a database).

Instead of making requests to github, the user will make requests to the node server (with the session token), which will in turn use the provided session token to look up the access token and make the request to github on the server side.

session token is mapped to the access token in a database and used to return user info

I have written more about sessions and cookies here.

OAuth Query State

While sending the user to the authorization URL, there is a provision to provide a value for a query parameter called state. The value of this should be a random un-guessable string provided by the application.

When github calls the redirect url, it will attach this state variable to the request params. The new URL would now look like:

https://github.com/login/oauth/authorize?client_id=myclientid123&redirect_uri=http://localhost:8080/oauth/redirect&state=somerandomstring

The application can now compare this value with the value it originally generated. If they are not the same, that means the request came from some third party, and should be rejected.

the redirect URL now contains a state that the client can compare with localStorage

This helps protect our application agains cross site request forgery attacks.


Like what I write? Join my mailing list, and I'll let you know whenever I write another post. No spam, I promise!

Comments

Soham Kamani

Written by Soham Kamani, an author,and a full-stack developer who has extensive experience in the JavaScript ecosystem, and building large scale applications in Go. He is an open source enthusiast and an avid blogger. You should follow him on Twitter