You (Probably) Don't Need That Auth Library
Published • Last Updated • 3 minutes
If you have ever visited r/nextjs, you have probably seen a post with someone complaining about an auth library. "NextAuth.js has bad or incomplete documentation", "Clerk (or Auth0) is too expensive", etc. One question I have not seen is, "Do I even need these libraries?" or better yet, "Why do I need these libraries?".
I asked myself the same question a while back when first looking into setting up single sign-on via Facebook Login and Google Login in a PHP application1. What I learned surprised me.
The Surprise
When I looked into how existing libraries worked, I found that it boiled down to only two URLs. Two!
But wait, you might be wondering, "Is he crazy? How could it be so easy if there are libraries like NextAuth.js and services like Auth0?"
The Problem
Before we can answer this, let's look at this table of auth providers:
Provider | OAuth Protocol Version | Documentation |
---|---|---|
Apple | 2.0 | developer.apple.com |
Discord | 2.0 | discord.com/developers |
2.0 | developers.facebook.com | |
GitHub | 2.0 | docs.github.com |
2.0 | developers.google.com | |
2.0 | developers.facebook.com | |
2.0 | learn.microsoft.com | |
Microsoft | 2.0 | learn.microsoft.com |
PayPal | 2.0 | developer.paypal.com |
Spotify | 2.0 | developer.spotify.com |
Twitch | 2.0 | dev.twitch.tv |
What do they have in common? They all share the same OAuth Protocol Version, which is the easier version to work with. But, as you can imagine, that wasn't always the case.
Let's take a look at these auth providers:
Provider | OAuth Protocol Version |
---|---|
Bitbucket | 1.0a, 2.0 |
Dropbox | 1.0, 2.0 |
Etsy | 1.0 |
Evernote | 1.0a |
Flickr | 1.0a |
Goodreads | 1.0 |
Netflix | 1.0a |
Okta | 1.0a, 2.0 |
OpenTable | 1.0a |
Trello | 1.0 |
Tumblr | 1.0a |
1.0a, 2.0 | |
WordPress.com | 1.0 |
See the difference? While some of them support 2.0, others have yet to make it past 1.0 or even 1.0a. And, for the sake of my mental well-being, I will not go into the difference between 1.0 and 2.0. Let's say that if you are considering writing your own logic, 2.0 is the only way to go.
The Solution
Now, let's take a look at those URLs.
/auth
This is the URL used to redirect you to the provider's authentication confirm page.
const client_id = 'xxxxx';
const redirect_uri = 'http://localhost:8080/oauth/google/callback';
const redirectUrl =
`https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=${redirect_uri}&client_id=${client_id}` +
`&access_type=offline&response_type=code&prompt=consent&scope=https://www.googleapis.com/auth/userinfo.email`;
/token with code
Once you confirm, the page will redirect you back to your app with a code
parameter in the URL's hash. You then use this code to fetch the tokens you will use to make further requests. Note that this token is single-use and only valid for about 30 seconds.
const code = 'xxxxx';
const client_id = 'xxxxx';
const client_secret = 'xxxxx';
const redirect_uri = 'http://localhost:8080/oauth/google/callback';
const response = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
body: JSON.stringify({
code,
client_id,
client_secret,
redirect_uri,
grant_type: 'authorization_code'
})
});
An example response from this request:
{
...
"access_token": "xxxxx",
"expires_in": 0,
"refresh_token": "xxxxx"
...
}
/token with refresh_token
And finally, when the access token you got from the request above expires, you can use the refresh token to get another one.
const access_token = 'xxxxx';
const response = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
body: JSON.stringify({
refresh_token,
client_id,
client_secret,
grant_type: 'refresh_token'
})
});
An example response from this request:
{
...
"access_token": "xxxxx",
"expires_in": 0,
"refresh_token": "xxxxx"
...
}
That's it!
And because I got sick and tired of digging for those URLs, I made a package for myself and others to use https://github.com/neogeek/create-app-oauth-providers.
But you don't have to use this package! In fact, I recommend you use it as a reference, as there is barely anything to it.