DEV Community

Cover image for OAuth2 From Scratch
Cole Hafner
Cole Hafner

Posted on

OAuth2 From Scratch

You see it everywhere: "login with < Google|Twitter|GitHub|Facebook|etc >". Personally I like it, A LOT. It's fast, easy, and saves me time. Who needs more passwords to remember? Heck, I used it to login to write this post.

Now it's time to find out how it works. Come with me, as we journey into the magical world of OAuth 2...

Alt Text

TLDR

OAuth 2 is easy to implement. There are just 2 steps: request a code and use that code to get a token. That's it. If you prefer reading this post in another language, I translated it to typescript.

Set up the client

I chose Google, but, since OAuth is a standard, it should be fairly similar for any provider. Here's how to set up an OAuth client with Google:

  1. Create a new project on Google dashboard Alt Text
  2. Enable APIs for that project. You can enable ANY service Google has: Drive, Gmail, Sheets, Docs, Voice-to-text, etc. The basic one you'll need is the Google People API, which provides information about user profiles Alt Text
  3. Create an OAuth client. This will be the client id/secret you need for requesting the oAuth token. First click 'Create Credentials' and then OAuth Client ID. It will prompt you to create a consent screen. Follow the prompts and then fill out the form like this. Make sure to set up the authorized domains and redirect domains. I'm using https://localhost, but you can use whatever you like, as long as it's HTTPS. It's easy to run HTTPS locally with this node package. Alt Text
  4. Copy the client id and secret for later use.

Get a Token

Now here's the fun part. It takes just 2 steps to get an OAuth token from Google:

Request an authorization code

The code is not THE token, it's called the authorization code and it's used to get the token later.

"That's dumb. Why not just send the token?" Good question. It used to be that way. It is called the Implicit Grant Type. It's a bad idea and generally not recommended any more (in some cases outright banned).

We'll be using the Authorization Code Grant Type. It takes one more step, but is more secure.

// GET request with the following params
{
   code_challenge_method: 'S256',
   scope: 'email profile', // tells google what info you want
   access_type: 'offline',
   response_type: 'code',
   client_id: clientId, // clientID from step 1
   redirect_uri: redirectUri, // page that handles token request
   code_challenge: challengeToken, // hashed/encoded PKCE challenge
   state: stateString, // random guid that will be passed back to you
}


// example
<a href="https://accounts.google.com/o/oauth2/v2/auth?code_challenge_method=S256&scope=email%20profile&access_type=offline&response_type=code&client_id=<client id>&redirect_uri=https://localhost/auth&code_challenge=o259Sjip6Cevgfe8RUw59jVO5z1mSzji_OuzIZFDTug&state=434595.10145617445">
   Login with Google
</a>

The aforementioned code_challenge parameter is from a method called PKCE. It stands for Proof Key for Code Exchange and is a security method to help make OAuth more secure. At its core, it's an hashed string that you send to the provider so it can verify your identity in the second step by sending the original string from which it was hashed. There's a real helpful node package that helps you generate PKCE challenges.

Request the OAuth Token

If all goes right on the first request, the provider will ask the user to login, generate the authorization code, and redirect to the uri specified in the redirect_uri param. It will include 2 important URL params: code and state.

Code is the authorization code needed to request the OAuth token.

State is the initial state param you sent in the last step. It's a simple mechanism to ensure the client can verify the identity of the server. If the state doesn't match, or isn't included, you can know not to trust that request.


// POST request with the following params
{
   code: authCode, // authorization code from the provider
   client_id: clientId, // id of the OAuth client
   client_secret: clientSecret, // secret of the OAuth client
   redirect_uri: redirectUri, // same from last request ¯\_(ツ)_/¯
   grant_type: 'authorization_code',
   code_verifier: codeVerifier, // raw PKCE token
}

// returns the following payload
{ 
   access_token: <access token>, // this can be used to query APIs
   refresh_token: <refresh token>, // can be used to get a new token
   expires_in: <expiration in seconds>, // usually set to an hour
   id_token: <id of the user>, // haven't really found a use for this
}

Use the Token!

You can then use the token to get data from Google on behalf of the user.

axios.get(
  'https://www.googleapis.com/oauth2/v2/userinfo',
  {
    headers: {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': `Bearer ${token}`
    }
  }
)

Google has a cool OAuth playground where you can try out all kinds of APIs.

That's it! You're done!

Resources

Here's a link to some of the resources I used to learn about this:

PKCE explanation: https://oauth.net/2/pkce/

PKCE node package: https://www.npmjs.com/package/pkce-challenge

Google OAuth Playground: https://developers.google.com/oauthplayground

OAuth2 Overview: https://aaronparecki.com/oauth-2-simplified/#web-server-apps

Google OAuth Walkthrough: https://developers.google.com/identity/protocols/OAuth2InstalledApp#obtainingaccesstokens

Sample GitHub repo: https://github.com/coleHafner/oauth-test/tree/parcel

Top comments (2)

Collapse
 
anderspk profile image
anderspk

How does this compare to using something like Firebase Authentication?

Collapse
 
colehafner profile image
Cole Hafner • Edited

That's a great question. I'm not sure. Guess Firebase will have to be my next post...