Using discord oauth2. A simple guide and an example nodeJS app

So it all started with a friend of mine really struggling with building an app with authorization through Discord. And since I remember myself having a lot of trouble understanding oauth flow for a first time, I figured I could help out and write a simple guide for everyone struggling with the same thing.

This guide is really basic and is aimed at explaining only the core concepts of oauth2 flow.

Introduction

First things first. Useful links:

Tools and tech used

Editor. You can use any editor to follow this guide. I, personally, use WebStorm for big projects and Atom for smaller stuff. But you can use any smart editor you prefer.

Backend. An example app will be built with a modern version of JS supported by nodeJS 7.10. It should be easy to understand if you had any kind of nodeJS (and expressjs) experience before, but I will explain some syntax things where needed.

If you don’t have nodeJS installed, you can grab the 7.10+ version on the official website

Frontend. The frontend will be built with pure ReactJS. The frontend is not a part of this guide, so I won’t explain how to create it here. If you’re not familiar with React and plan on creating anything for the frontend — you should definitely check it out! You can see the code of that in the project’s github repo.

Ok, enough of that let’s get started!

As much as I would like to get to coding, we should, at first, figure out how aouth2 works.

Basically the process looks like this

  • User clicks a link
  • User is redirected to Discord where he clicks on an “Authorize” button
  • User is redirected back to our service with code in the URL
  • Our service makes an authorized request to Discord to exchange code for tokens

And that’s it! Pretty simple, huh?

Note: we won’t really implement token refresh in this example as it involves saving user’s tokens and so on. But it shouldn’t be a problem to do yourself after completing this guide

Looking at the steps up there we can plan out our app. This is what we’ll need

  • A simple root URL that will send our index.html to the user to start the process
  • A URL that will redirect user to Discord with some data added to the request
  • Another URL called a “redirect” or “callback” URL, to which Discord will redirect our user after clicking “Authorize”

Let’s get to coding!

The guide

Creating a basic server

During the coding part I will post the pieces of code first and then explain them

Fist of all — create a new folder, open a terminal inside it and run

npm install express

This will, obviously, install expressjs framework, which is one of the many tools used to build APIs with node. I find express one of the simpler ones to use and support over time.

Now create a server.js file and paste this code in it

This is a very basic setup, that will run our app on port 50451 and send back index.html when you open the root localhost:50451 URL.

Creating an API

Now we can start building our simple API. Create an api folder and in it create a discord.js file.

So here we’re creating a router object, which basically allows us to attach functions to certain URLs, like we did with app.get() in our server.js the only difference is that router is used as a “middleware”.

Basically when our server receives a request — express looks through all the available routes and then triggers all the middlewares attached to it, until there is nothing else to trigger. You can read more about express router over here.

And here we’re getting CLIENT_ID and CLIENT_SECRET from our environmental variables into the app itself. process.env is a global object which can be accessed from anywhere in your node app.

The redirect variable simply encodes our “callback” URL so we can pass it to discord as a query string.

This is the most important part. Remember that “URL that will redirect user to Discord with some data added to the request”? Well that’s it. Here we’re using the new and fancy JS string templates to add our CLIENT_ID and redirect to the authorization URL.

A bit about string templates in JS:

Basically you can insert any variables into the string using a syntax like that

`Hi, ${username}`

Which is equivalent for

'Hi, ' + username

And back to our app

Now we have our discord.js module, but it’s not connected to our server. To do that we’ll need to add a reference to it into our server.js

Just add the code above after everything else in server.js

Basically we told express to use the router inside discord.js for any requests that match /api/discord URL.

Before we can test our app at this state — we need to create an app at Discord, as well as create a basic index.html file.

Creating a Discord app

Click here to open app creation page. You should see something like this:

Discord’s app creation form

Fill in App Name field, and click the Add Redirect button. Which is the exact URL to which Discord will send our user after clicking “Authorize”.

For our example use this URL:

http://localhost:50451/api/discord/callback

We did not implement it yet, but that’s fine, we’ll do it later.

Click “Create App” and you should see something like this.

Copy the Client ID and Client Secret somewhere safe for now. We won’t need to open Discord website anymore, so you can close it.

Important: Never share your secret anywhere publicly. It’s called secret for a reason!

Creating an index.html

So we created our basic server, now we need to create a page with a link user can click to authorize with discord.

Create an index.html file beside your server.js and paste this code:

If you double click on it — you should see something like this in your browser;

We’re almost ready to launch our server, but we need to set our CLIENT_ID and CLIENT_SECRET environmental variables. If you know how to do that — go for it. If you don’t know, here are the guides and stackoverflow answers for different OS:

Now we’re ready to launch our server! Just open a terminal in a folder with your server.js and run this command

node server.js

You should get this message if everything went fine

Running on port 50451

Now you can open localhost:50451 in your browser and see the same index.html we created earlier.

If you’ll click the link — it should get you to the page like this:

If it shows an error instead — that probably means that you didn’t set your evn vars correctly.

If you click authorize — you’ll get an error:

cannot GET /api/discord/callback

Since we didn’t implement it yet. So let’s do just that!

Creating a callback handler

We will need a couple of extra things to create a callback handler. Run this command

npm install node-fetch btoa

node-fetch is a package that allows us to use a new and fancy fetch API to perform HTTP requests. I like it for it’s simplicity and unified syntax between serverside and the browser.

btoa allows us to use the browsers’ btoa base64 encoding from browsers in our backend code.

Now add it to the requirements on top of discord.js like this

Don’t worry about catchAsync for now, I will explain it later.

And now, after our /login handler we’ll create our /callback handler. To do that — paste this code into discord.js

This is quite a bit of code, let’s see what we’re doing here, line-by line.

async/await: I’m a fan of the new async/await syntax in JS, but it requires us to add an extra error catcher on top, so we won’t loose our errors. That is achieved by wrapping our handler inside a catchAsync function, which I will show later in the guide.

Line 2: we’re checking that Discord send us the authorization_code by setting a code query string. If not — we’ll throw an error;
Line 3: we extract our code from the URL.
Line 4: we encode our CLIENT_ID and CLIENT_SECRET as a Base64 string (that’s what we needed btoa package for), it’ll be used to authorize our token exchange request.
Lines 5–11: we perform a request to Discord’s token exchange URL adding the code we got and our redirect_uri which is the same as in the /login URL.
According to the RFC 6749 this should be a POST request with a Basic HTTP authorization header.
Line 12: we convert the raw response to JSON we can read.
Line 13: we redirect our user to the home page of our app with the token we got set in the URL (to show it on the webpage)

Note: In the real app you wouldn’t want to pass such a sensitive info in the URL, but rather set it as a cookie or just return it as a response to your app that will then show it to the user in an async way.

Adding a global error catcher

Before we finish up here — we need to do a couple more things so our app won’t loose it’s errors.

Express allows us to add a middleware that will catch all the errors thrown by our app. To do that you’ll need to add a new middleware with app.use() in the very end of your app.

Open server.js and paste this after everything else:

Basically here we use a basic switch syntax to check which error we got and send a message to our client based on that.

That’s good and all, but when using async/await in JS, if you don’t wrap your function in try{...} catch(e) {...} — your error’s will just disappear, causing your server to throw an “unhandled promise rejection” error and closing the app with a non-zero exit code;

To fix that we need to create a little wrapper. Make a new file called utils.js right beside your server.js and paste this into it:

This is really basic and you all it does is adds a catch to our route handler, which then calls our error catching middleware we just created with next(err)

Running the app

And now you’re good to go! Just run

node server.js

Click the link at localhost:50451, authorize your app and then you should land on the page with the URL that looks something like this:

http://localhost:50451/?token=33JjS8512HebOWuP9vo9IMrf1Wy7y8
Note: If something goes wrong — you should see an error name in your terminal. With that you can find an explanation on the RFC 6749 website.

As you might’ve guessed — that is your access token! Which you can use to authorize a request to http://discordapp.com/api/users/@me that will return username of the authorized user.

I used Postman to make a request to Discord API
Tip: To make an authorized request with the token you just recieved — you need to add an “Authorization” header to your request that looks like this: Bearer <your_token_here>

What’s next?

Token refresh

There are a lot of things to add here. First of all — tokens rotation. The basic algorithm for that should look like this:

  • Save access_token and refresh_token somewhere, like a DB, or just a JSON file.
  • Check the response code for your authorized Discord API requests.
  • If the code is 401 — make a new request to the same /token endpoint as in our /callback but with our refresh_token instead of code
  • Save the new access_token and refresh_token and use them with your requests.
Note: Discord’s tokens last 7 days

Some API requests

You didn’t get your token for nothing, right? You wanted to make some Discord API requests!

Got to Discord’s dev documentation to see what you can do with it!

UI

We also want our users to looks at something beautiful while using our app.

You can find a simple React UI with use of CSS-Modules and a basic BEM-methodology in the project’s github repo. Check it out!

Conclusion

That was a pretty lengthy tutorial, but, as I said, I remember myself banging my head against the wall trying to understand oauth2. So I tried to explain all the things necessary to get the basic concepts.

I’m sure you could be more efficient with the example app we’ve built today. But I think it gets the job done in terms of showing how everything works.

If you have any questions — hit me up in the comments section or on twitter. And don’t forget to check the sources of the app on github. They have a lot nicer UI than what we’ve got here ;)

A simple React UI made for this project

Anyways, thanks for reading, and I wish you a good luck with your own apps utilizing Discord’s oauth2!

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.