Read time ~ 3 coffees ☕☕☕
I recently started working on a project which allows Reddit users to schedule posts for multiple subreddits. To post something on behalf of the user, many services support the OAuth2 protocol for authorization. Reddit does too. Reddit supports 3 types of applications under this flow. The one we are interested in is the web app one. Refer to this doc if you are confused about choosing the right type.
Create an application
If you have created an application in Reddit, then you can skip this step.
Head over to https://www.reddit.com/prefs/apps

Click on the button to create a new app.

Fill in the details and hit create an app. Few things to note.
- The app is of type web type.
- Redirect URI is important so please make a note of it before saving. I generally put something like
http://localhost:3000/login/callbackthere.
Congrats, you successfully created an application. 🎉
Before we move on let’s first try to understand the flow. This will help us understand the significance of redirect_uri
The OAuth Flow

The diagram above describes the OAuth flow. Let’s go over the flow once and then dive into the code.
- The user visits the Web Application and clicks on the “Login With Reddit” button.
- The user is then taken to Reddit and is asked to either deny or approve the permissions requested by the application.
- If the user approves, then the user is redirected to the
redirect_uri(Remember this?). This will redirect the user back to the web application. - The
redirect_uriredirects the user back to the web app along with 2 things.codeandstate. We will talk aboutstatelater. - The web app then makes requests to the Reddit API for the user’s
access_token. Thecodevalue received in the previous step is passed as a parameter in this request. - Reddit validates the
codepassed and then returns anaccess_tokenand arefresh_token. - The web app saves the
access_tokenand now can make requests to the Reddit API on behalf of the user.
Some OAuth providers return an access_token after step 3. But for Reddit, we need to perform a few more steps.
The Code.
function Home(props) {
function openLogin() {
window.open(
`https://www.reddit.com/api/v1/authorize?
client_id=YOUR_CLIENT_ID&response_type=code
&state=random-string
&redirect_uri=http://localhost:3000/login/callback
&duration=permanent&scope=identity,submit,save`,
"_self"
)
}
return (
...
<Button onClick={() => {openLogin()}}>Login With Reddit</Button>
....
)
}
This is a react component. But it can be anything. The important thing is the user should be redirected to Reddit after clicking the button.
A few things to note.
- Here, we redirect the user to the
/authorizeendpoint. We pass a few query parameters. Let’s go over them quickly.client_idThis is the string that you can see below the name of the application we created in step 1.response_typeThis needs to be set tocodeYou can read more on this here.stateThis is a random string that you pass to the endpoint. This same string is returned later when Reddit redirects the user to theredirect_uri. This field is important to understand if the web app has received a response to a request that it initiated.redirect_uriThis should match with the one in step 1.durationThis defines for how long do you need theaccess_token. Allaccess_tokenexpire after 1 hour. More on this here.scopeThis defines the permissions the web app needs, to function properly. A list along with their description of each scope is available here https://www.reddit.com/api/v1/scopes
The user will be presented with the permission screen like below if everything goes right.

Users can either Allow or Decline access to the web application.
Let’s consider the happy path ie. user allows access. In both cases, the user is redirected to redirect_uri
function LoginWithReddit({location}) {
let {search} = useLocation()
useEffect(() => {
const query = new URLSearchParams(search);
axios.get(`http://localhost:3003/login_reddit?code=${query.get("code")}`)
})
return (
<Flex>
<CircularProgress isIndeterminate color="orange"/>
</Flex>
)
}
// This is how react router is setup.
<BrowserRouter>
<Routes>
<Route path="/login/callback" element={<LoginWithReddit/>} />
<Route path="/" element={<Home/>} />
</Routes>
</BrowserRouter>
This is the react component that gets rendered when the user is redirected back to the web app.
Things to note
- The
codereceived in the query parameter is sent to the back end. The back end then requests the Reddit API to get the necessary tokens. - An alternative approach could be to redirect the user to the back-end directly, which will save an API call.
Back-end server code
... // express server boilerplate
app.get("/login_reddit", async(req,res) => {
try {
// GET USER ACCESS TOKEN
const code = req.query.code;
const encodedHeader = Buffer.from(`${process.env.REDDIT_CLIENT_ID}:${process.env.REDDIT_CLIENT_SECRET}`).toString("base64")
let response = await fetch(`https://www.reddit.com/api/v1/access_token`, {
method: 'POST',
body: `grant_type=authorization_code&code=${code}&redirect_uri=http://localhost:3000/login/callback`,
headers: {authorization: `Basic ${encodedHeader}`, 'Content-Type': 'application/x-www-form-urlencoded'}
});
let body = await response.json();
// GET USER INFORMATION
response = await fetch(`https://oauth.reddit.com/api/v1/me`, {
method: "GET",
headers: {authorization: `bearer ${body.access_token}`}
})
let user = await response.json();
await signInUser(user['id'],user['name'],body['access_token'],body['refresh_token'])
let token = await signJWT(user['id'])
res.send({token})
} catch(e) {
res.send({msg: "Something went wrong."})
}
})
... // other endpoints
This is a code snippet from an express server that exposes GET /login_reddit endpoint.
Things to note
Getting user tokens POST /api/v1/access_token
- Reddit uses HTTP Basic Authentication for fetching user access and refresh tokens. This authentication mechanism requires that the client sends base64 encoded credentials in the
Authorizationheader inBasic [base64 encoded credentials here]format. - 3 things need to be passed in the body here
grant_typeThis needs to be set toauthorization_codecodeThis needs to be set to the value returned by Reddit after redirecting the user to theredirect_uriredirect_uriI don’t know why we need to pass this again 🤷🏻
- Along with the
Authorizationheader, theContent-Typeheader needs to be set toapplication/x-www-form-urlencodedThis is super important. You might also need to setUser-Agent. This is generally the name of your application. - The response finally returns the tokens. An
access_tokenand arefresh_token. Access tokens are valid only for 1 hour. To get a freshaccess_token,refresh_tokenis used.
Getting User Information GET api/v1/me
- To get user information, the request needs to be made to Reddit OAuth URL. So instead of
https://www.reddit.comrequest needs to be sent tohttps://oauth.reddit.comPlease make sure you are sending all your requests (for protected endpoints) to this URL. Otherwise, you will end up with 401 Unauthorized responses. - The
Authorizationheader is set to theaccess_tokenthat was sent in the above step. Reddit follows the bearer schema which means the header should be set to a value of the formatbearer [access_token_here]
Logging in the user
- Once the user information is fetched from Reddit. A JWT can be created and sent back to the browser which will then log in the user into the web application.
‼️ JWT isn’t secure. It is just a base64 encoded string. Storing sensitive information like passwords in these tokens could pose a huge security risk.
To re-issue access tokens after they have expired can be done by calling /api/access_token It is pretty straight forward and more information can be found here. There can be a few strategies to get fresh access tokens.
- A cron job can be set up that checks and fetches fresh access tokens for the ones that have expired. The downside I can see to this approach is this will fetch fresh access tokens for accounts that are inactive thus wasting precious API requests. Also as the app scales, a single cron job might not be sufficient.
- Fresh access tokens can be fetched on-demand. If Reddit API returns an expired access token error then a fresh access token can be fetched. This ensures that requests are made only for active users.
And that’s it. The web app can now call protected Reddit API endpoints on behalf of the user.
Comment if you have any questions. You can follow me on Twitter. I usually tweet about JavaScript.
If you are learning React then you might be interested in Writing useState hook from scratch.
I also make YouTube videos. Go, check them out!