3 Steps to OAuth (with Code & Examples!)
Tags: webdev, javascript, react, nextjs
Published: 2023-02-17 16:29:36

## Introduction

So... what is this... "OAuth" huh?  
This is what they say:

> OAuth <mark>is an open-standard</mark> authorization **protocol** or framework that provides applications the ability for secure designated access.

Gibberish, let me break this down in layman's terms

![dev.to login page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ehmrm4vrc9t9ks7oqly2.png)

This is DEV's login page ☝🏻

The buttons you see here - "Continue with Apple / Forem / GitHub / Twitter" use **The OAuth Flow** to redirect you to a third-party service that will identify you *for* Dev.to (*while you allow yourself to be identified by this service & give read access of your profile to Dev.to*) and this service will then redirect you back to Dev.to's website saying "*here are the credentials of this person who wants to signup on your website, we've verified him, you're good to go*"

In this post, we'll implement this OAuth flow using **GitHub** & **NextJS.**

Let's do this!

## Idea Behind OAuth

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8eszzfznjg8uiy0w6yyh.png)

Let's say you're making a tweet scheduler app,  
They come on your app, write a tweet & schedule it,  
It's your job now to save that tweet & later post it on Twitter *on their behalf.*

You're in a dilemma now:

* On the one hand, you can't ask the user for their account name & password just to make a tweet because they wouldn't trust your app.
    
* On the other hand, if you go to Twitter saying "hey Twitter, a user with the name `@johndoe` wants to post this..."  
    Twitter simply wouldn't allow that because anyone can tweet on anyone else's behalf then!
    

This is where the *Standard OAuth Protocol* comes in.

You redirect the user to Twitter's website and indicate what you want access for, <mark>user now has the power to authorize your app or disallow it from having tweet post access to your account</mark>,  
Twitter will then accordingly redirect the user back to your website giving you either `code` or an error.

The `code` is a string of utf-8 characters that Twitter generates and it is used to Identify:

1. Your app
    
2. The user
    
3. The permissions user has agreed to authorize your app.
    

> Note: This flow is kept a standard for all services that can help identify people & authorize other apps on users' behalf to read/write data.

## Enough talking Let's Code!

```bash
yarn create next-app
```

This command will create a starter NextJS app,  
Name your app, select JavaScript or TypeScript (doesn't really matter here, I went with TypeScript),  
And you're all set up!

### Why did I choose NextJS?

Well you see, implementing OAuth requires a server-side node API endpoint and with this framework, we can have all our server-side (Node) & client-side (React) logic in one place.

You're welcome to use any other backend framework here as long as it follows the same logic we'll be using in this post.

There remains one more question: "<mark>Why do I need a server-side endpoint anyway?</mark>" to which I'd suggest you to read along as it unfolds.  
(hint: for secure communication between server & oauth service)

Now, I need you to head over to your GitHub account &gt; Settings &gt; Developer Settings &gt; OAuth Apps and Create a new oauth app


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/asbhnnvbpabjgz4k5sb8.png)

The most important field here is the "Authorization Callback URL" which we set to our server-side API endpoint - `http://localhost:3000/api/auth`

We are to make a file `auth.ts` under the `api` folder yet.

This is the URL GitHub will redirect the user to, after the user *authorizes* or *rejects* your authorization request.

Go ahead, register your application & then generate the client secret

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5cgqubqqgnqynthal0mo.png)

<mark>GitHub needs the client id &amp; client secret to identify your app and redirect the user to the appropriate callback URLs</mark>

Now, let us head over to the NextJS app & create a `.env` to store client id & secret in one place.

### Why store secrets in a .env file instead of let's say a "config.js" file?

The first & foremost reason is that you can add `.env` to a `.gitignore` and it will be skipped by git when hosting the code on GitHub.

Now, when you deploy the code to some service like Vercel, they ask for "Environment Variables" and you can just add the client id & secret over there.

This serves 2 important purposes:

1. No one else can steal your GitHub OAuth credentials from code (pretty evident)
    
2. You'll have to create another oauth app (*this is GitHub-specific by the way*) with the authorization callback URL set to your site's hosted URL in GitHub because you'd be hosting your web app & if you use your old OAuth credentials, it would simply redirect the user to `http://localhost:3000/api/auth` instead of `https://your-hosted-app-url.vercel.app/api/auth` on authorization requests from the hosted version.  
    (This is why `.env` files are used extensively for *different environments*!)
    


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vg609jcro37xc9l9assy.png)

#### **" oi oi oi oi oi.... why's there a** `NEXT_PUBLIC_` **there? "**

Oh, you caught me slippin' that one, eh!

As you know, adding `NEXT_PUBLIC_` after the env file variable names tell NextJS that we wanna "expose" that env variable to the client side React app, Not adding this prefix in a variable means that we don't wanna use that variable or "expose" it on the client side.

This is where the role of the server-side API endpoint comes in,  
You see, the GitHub client secret is like a password & the client id, you can think of it as a username to help GitHub identify your app.

You can show your username to the public but it wouldn't be preferable to publicly expose your password just like the username,  
If we add `NEXT_PUBLIC_` in front of a sensitive env file variable, NextJS while building your app bundle (i.e the final javascript file to be sent to the user's browser) will include that variable in the file source code too.

If you're patient enough to read through the javascript responses of a page, you can easily find it and someone with malicious intents can easily exploit this exposed variable!

However, you can still use the env variable without the `NEXT_PUBLIC_` but only on the server side.

This is why we'll be using the server for all the communications with GitHub API which includes sending client secret along with our requests.

Honestly idk why I named this section "*Enough talking Let's Code!*" cuz we're still talking, but promise me, the next line will be code!

.

.

.

Hah! it wasn't 🤣

Jokes apart, let's start by building the URL that GitHub requires our app to redirect users in order to start the authorization process.

From here, I'll be referring to GitHub's own docs for performing OAuth requests, you can check them out here👇🏻  
[https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps)

### Step 1: Redirect User to Authorization URL

```javascript
const oauthUri = `https://github.com/login/oauth/authorize?client_id=${process.env.NEXT_PUBLIC_GH_CLIENT_ID}&scope=user:email,read:user&redirect_uri=http://localhost:3000/api/auth`
```

For the sake of not talking extra, I've hard-coded the `redirect_uri` to `http://localhost:3000/api/auth` that should actually be stored in a `.env` file and changed according to the environments in which the app is being hosted and the associated authorization callback URL.

Coming to the `scope` param of the URL, we've provided a list of scopes: **read:user** & **user:email** to it which means we want read access to the user's details (username, name, etc.) & their email.

The code looks something like this:

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9gfg6i43rvmpx7h5yahd.png)

After running the app in dev mode & clicking the link "Login with GitHub" on the page, we are redirected to this page:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ycp23xgwdm8a04xwzvof.png)

Have a close look at this page, it clearly describes an App named *Test* made by a GitHub user *Kuvam Bhardwaj* wants your GitHub profile data (email & other public info) and clearly states the access is read-only.

Refer to this url for more info on all available `scopes` 👇🏻  
[https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps](https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps)

Seeing this page is generally a big relief as we didn't mess up the setup in GitHub developer settings ✌🏻

Authorize the app & if you correctly put the URL in the authorization callback URL in GitHub settings, you should be redirected to the specified URL (`/api/auth`)


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qfwm9lgxpxst60e8ecfm.png)


Notice the URL address bar contains some kind of `code` following a string of random characters,  
This, ladies & gentlemen, is how victory looks, a string of hexadecimal characters 🥂

Let's now create a file named `auth.ts` under `api` folder to actually handle this victory!

### Step 2: Exchanging the Code for an Access token

After authorizing, GitHub redirects the user to our specified URL which is actually a server-side API endpoint.

This API endpoint will make a POST request to GitHub's OAuth API sending the obtained `code` and client secret as the payload, in response, we'll probably receive an `access_token` if the `code` was valid.

On receiving the `access_token` we can start making requests on behalf of the user, in this case, getting their email & public info.

If the scopes we previously specified were of editing user's personal data, followers management or even repo creation/deletion, we would be able to do that too with this `access_token` though you might have to search the docs for finding a suitable API endpoint that does that.

> You would get errors if you make requests to API endpoints that require additional scopes than you've specified.
> 
> Referring GitHub's API docs is the best way to know what endpoints require which scopes.
> 
> Or... just... refer to docs in general... they're helpful!

**In our simple example, we'll simply exchange** `code` **for** `access_token`**, fetch the user's profile data with it, print it on the console and redirect the user to the homepage.**

This could easily be scaled to do obvious things like:

* save the user's data in a database after fetch
    
* set a cookie to identify the user by their database ID
    
* redirect to some dashboard or some other personalized page after authenticating them with cookies
    

Let us look at the code for exchanging the `code` for `acess_token`


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tillvcm0wrogizd9p189.png)


Ahem... please excuse my copilot prompts...

In the above code, when the user lands on `/api/auth` we grab the `code` embedded in the URL & send it in the POST request body along with other variables like `client_id` & `client_secret` to get back the `access_token` from GitHub.

Headers in the fetch request, as hinted by their name, say to GitHub's API that we are sending a body in JSON format (`Content-Type`) & we want a response in a JSON format (`Accept`).

For some reason, GitHub defaults to sending a string looking something like this unless we explicitly provide the `Accept` header in the POST request

```javascript
"access_token=gho_16C7e42F292c6912E7710c838347Ae178B4a&scope=repo%2Cgist&token_type=bearer"
```

Our work is partially done now, we have the `access_token` and just need to use this and get the user's profile details on their behalf.

### Step 3: Use the Access token to Perform Actions on user's behalf


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kmubrifx1fzu1xxshodv.png)


After getting `access_token` we use it in the header as `Authorization: Bearer access-token...` & request the GitHub API to grant us their profile data.

Let's now click on the "Login with GitHub" button on the index page again!


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hr7cix38g3mfksylxq1k.png)


![touching myself ryan reynolds GIF by Deadpool's Fun Sack](https://media0.giphy.com/media/l4JyV4nyvJj834IZW/giphy.gif?cid=ecf05e477itov35nqqpp6ikm8pleh6umrf559dmbge10nsbx&rid=giphy.gif&ct=g align="center")

## Generalizing GitHub-specific Patterns for other OAuth Providers

So, until now we've been very specific to GitHub, but if you refer to docs of other OAuth providers like LinkedIn or Google or Facebook,

You'll notice this pattern where:

1. You get your personalized client id & client secret
    
2. Redirect the user to a link with the client id, redirect uri & scopes or access permissions embedded in it
    
3. The user either authorizes or cancels the authorization request and you get the user redirected to your site with an error or some kind of code
    
4. You then exchange this code for an `access_token`
    
5. Finally, you start making requests to the service on behalf of the user with the `access_token`
    

If you've used any BaaS platform like firebase or supabase before, then you can distinctively point out what's happening!

For example here in supabase when I go ahead to setup Authentication with Social login via Google:

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sehvnq2havokd7y2ue70.png)

It asks me to enter my client id, and client secret & gives me a URL to add in redirect URIs in Google's OAuth setup process,

Supabase will manage the rest on its own, all the callbacks, redirecting users to your app, everything!

I might have to add that you still need to provide YOUR APP's URL so that supabase itself can redirect the user to your web app post-authentication.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0gmeq1tasxsdh485pedk.png)

Here's Google's OAuth setup screen for reference


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/19k8ar4gpnibre3amqan.png)

And you might as well find the docs that list all the scopes for authorization along with all the minute details that are Google-specific!

See ya! ✌🏻

---

Thanks for reading!  
If you liked the article, consider dropping some emojis ♥️🔥💯✨

But, if you loved it?  
I post about the web every day on Twitter 👉🏻 [bhardwajkuvam](https://twitter.com/BhardwajKuvam)  
Consider giving me a follow there ♥️