This article may contain affiliate links. If you buy some products using those links, I may receive monetary benefits. See affiliate disclosure here
There won’t be any internet user who haven’t yet come across a ‘Signup with Google’, or ‘Signup with Facebook’, button while trying to access web services.
As normal users, we don’t think much about it. We just click the button that takes us to Facebook or Google, where we login. Then we’ll be presented with an option to either allow or deny the app to access your details. That’s it.
Then we’ll be redirected back, and Voila!
The website/app now greets us with the name, username, and email that we’ve given in our Google/Facebook account. No registration, no password generation, nothing.
But does that mean the third-party application can do anything with our Google account? Have they got the passwords as well?
You won’t be having these questions if you carefully read the message. It clearly says what all information is shared with the third-party app – sometimes it allows just reading your profile information (id, username, email, profile image), other time it may allow doing some actions on your behalf (like an app posting to your Facebook profile). The point is that the access is limited – it never gives full authority over your account.
As a normal user, this knowledge is more than enough. But as a web developer, I wanted to know more.
OAuth comes in…
And that brought me to OAuth. It is not a library or framework for implementing authentication. Instead, it is a set of standards for implementing authorization delegation.
In simple words, it allows apps to move (delegate) the authentication part to popular service like Google, Facebook, Twitter, GitHub, etc.
It’s a win-win for both the app developer and the user:
- The user need not create and remember one more password, they can use their existing Google, FB, or whatever identity for signing up and logging in.
- The app developer got rid of the headache of managing credentials, hashing the password, authentication and so on. There is even a saying – ‘a bad person can never get your password if you don’t have a password.’
These days, there are libraries in most web programming languages that allow you to integrate OAuth flow into your apps without knowing much about what is going on behind the scenes.
The NextAuth library is a great example. It enables you implement OAuth with almost all providers in your Next.js application without even know what an authorization code is, what an access token is, and what not – how to even set up an Authorization Header in a POST request.
Its it true that libraries and frameworks helps you to do things faster while following best practices.
But for me it takes away all the fun. I can’t appreciate a thing if I am not aware of what’s going on under the hood.
Here is how it goes…
An OAuth authentication is an agreement between three parties – you (the end-user), the third-party application (a.k.a the Client – for e.g., Spotify), and the OAuth Provider (Google, Facebook, etc).
If we imagine it as a conversation between these three people, here is how it goes:
Scene 1: Spotify registration page
Me: Hello, I’d like to sign up for your application.
Spotify: That’s awesome! To make things easy, you can use your Google account for authentication. How does that sound?
Me: Sure, I already have a Google account.
Spotify: Perfect! My app is all set up to work with Google. Are you ready to get started?
Me: Absolutely, what do I need to do?
Spotify: Simple! Just hit the button that takes you to Google’s login page on my behalf. Google will guide you through the rest.
Me: Got it, I’m clicking the button…
Scene 2: Google Login Page
Google: Welcome! Looks like you’re coming from Spotify.
Me: Yes, that’s right!
Google: Spotify is registered here. You’re allowing them to access certain details from your Google account, correct?
Me: Exactly, that’s what I want.
Google: But who are you? Have we met before?
Me: I actually have an account with Google.
Google: Great! Please log in. If you don’t have an account yet, don’t worry—you can create one right now.
Me: I’ll log in. Here’s my Gmail ID and password.
Scene 3: Google Consent Screen
Google: Awesome! Now I remember you. You’re authorizing Spotify to access your profile information: your ID, name, email address, and profile picture. This grants Spotify limited access to these details. Is that correct?
Me: Yes, exactly.
Google: Okay, I’ll take care of that. Rest assured, your Google account will remain secure. You can revoke Spotify’s access at any time if you change your mind.
Me: I appreciate that.
Google: If you’re ready, hit the “Allow” button. If not, you can click “Deny.” Once you allow it, I’ll generate an authorization code and send it to Spotify’s website along with you. From there, Spotify will handle the rest.
Me: Done, I clicked “Allow!”
Scene 4: Spotify’s Welcome Page
Spotify: Welcome, Abhinav! Thank you for joining us.
Me: That was surprisingly smooth. How does this magic work?
Spotify: What you just experienced is called OAuth. After you returned from Google, they sent me an authorization code directly. I used that code to obtain an access token. This token is like your ID card with Google. It allows me to fetch your information when needed. So, I got your name, ID, email, and photo using the token.
Me: That’s fascinating! But will I have to do this every time I log in?
Spotify: Nope, that’s the beauty of it. The token is reusable. I’ve securely stored it in your browser, like a secret key. So, the next time you visit using the same browser, I’ll quietly use that token to authenticate you with Google. It’s all behind the scenes. As long as the token is valid, you’ll be logged in automatically.
Me: This is pretty cool stuff!
1. Creating a OAuth Client ID & Secret
- With any provider, the first step is registering your client application with the OAuth provider.
- The complexity of the registration process can vary from provider to provider. However, there are a few things that are common among all – you have to provide client application name, home page url, and a redirect url.
- The redirect url is the url to which the provider sends the authorization code once the user has successfully logged in and allowed access. So make sure it is secure and belongs to you.
- Once you’ve registered, you will get a client ID and a secret. These are strings that look gibberish. Out of the two, the client secret should always be kept confidential. So you should store them as environment variables instead of hard coding them in the application code. Because application code usually goes into public git repositories, while environment variable files are git-ignored.
- The client id is publicly known, but make sure to keep the secret key private.
2. Redirecting the User to the Auth Provider
- In the signup page of you application, or wherever you want to authenticate the user, create a link to the provider’s authentication page.
- You’ll add your client id as a query string so that the provider knows which application is requesting access.
- In addition to that, you need to add something called scope as well. Scope determines the level of access you’re requesting to the user’s account. For simple signup and login purpose, you only need to request the basic profile information – name, id, email address, and profile picture.
- The scope string also varies from provider to provider. For example, ‘userinfo.profile’ is the scope string that allows fetching a user’s basic profile information from Google. Whereas for GitHub, the equivalent is ‘read:user’.
Here is how a redirect to Google looks like (in PHP):
<a href="https://accounts.google.com/o/oauth2/auth?client_id=<?= GOOGLE_CLIENT_ID ?>&redirect_uri=<?= urlencode(GOOGLE_REDIRECT_URI) ?>&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email&access_type=offline">Login with Google</a>
And here is one for GitHub:
<a href="https://github.com/login/oauth/authorize?client_id=<?= GITHUB_CLIENT_ID ?>&scope=user">Login with Github</a>
- There can also be scopes that allow more privileges. For instance, if you are creating a social media management tool like Buffer, your application might want to publish post’s on the user’s behalf.
- But for now, let’s keep things simple – so a minimal scope like ‘read:user’, or ‘userinfo.profile’ is all that is required to perform signing up, setting up an account, and login on the client application. Because it fetches unique info like the user’s id, and email address from the provider.
- Then you can use that same ID or email to uniquely identify the user on your app as well.
3. The User Authenticating and Giving Consent
- Once the user is redirected to the provider’s website, the user will be asked to login if they are not already logged in.
- The name of the client application will also be shown since we had included that in the redirect URI as a query parameter. You can see that in the address bar as well.
- Once the user is successfully authenticated, the next step is authorization, that is, granting access. The user can see and verify the list of things that we’ve asked for. That is, the things included in the scope parameter.
- Once the user allows access, the provider generates an authorization code, and redirects the user back the redirect_uri. It is also known as the callback URI.
4. Receiving the Authorization Code
- When the user is redirected to the callback URI on the client app, a query string will also be attached.
- Either it contains the authorization code, or it contains an error code.
- Let’s assume that everything is fine, and the authorization code is received.
- Based on the programming language you’re using, extract the code from the query string.
5. Exchanging the Authorization Code for an Access Token
- Now the authorization code is known only to the provider, the client app, and the user.
- It is usually for one-time use, and very short lived. Short-lived in the sense, one minute, 10 minutes, like that. This is for security reasons, so the authorization code cannot be misused later.
- The authorization code does not directly give access to anything. So as soon as it is received, send a POST request back to the provider along with the code and most importantly, the client secret.
- This request is made safely from the server. So even the user will not be able to know the client’s secret key. It’s known only to the client and the auth provider.
- Use cURL or any other tool available in your server-side programming language to perform the request safely.
- There the provider already knows the code it generated, which client is accessing which user, and the requested permissions, or scope.
- If the code is still valid, the provider generates an access token, and optionally a refresh token as well, and sends it back to the client application server. The response is often in JSON format.
Here is a sample code in PHP that sends a cURL POST request to /token
endpoint along with the auth code:
<?php
// store secret keys in environment variable
include './constants.php';
$data = array(
'code' => GOOGLE_AUTH_CODE,
'client_id' => GOOGLE_CLIENT_ID,
'client_secret' => GOOGLE_CLIENT_SECRET,
'redirect_uri' => GOOGLE_REDIRECT_URI,
'grant_type' => 'authorization_code',
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://oauth2.googleapis.com/token');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$token_data = json_decode($response, true);
var_dump($token_data);
6. Requesting Data from the Auth Provider with the Access Token
- Now that the client app has the access token, it can use that to fetch the required information about the authenticated user from the provider.
- The endpoint to which the request needs to be sent varies depending on the provider.
- For Google, it looks like this – https://openidconnect.googleapis.com/v1/userinfo, and for GitHub, it looks like this – https://api.github.com/user.
- If you want to integrate Google, then check out the OAuth playground. You’ll get a hang of how all these things work.
- Also, the access token is usually sent along as a Bearer token in the request header. It’s the recommended way. Sending it in the query string may also be possible, but it’s discouraged due to security concerns. Because URLs can also end up in log files, thereby exposing the access token.
<?php
include './constants.php';
$api_url = 'https://openidconnect.googleapis.com/v1/userinfo';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_url);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Authorization: Bearer ' . GOOGLE_ACCESS_TOKEN,
'Accept: application/json'
));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$user_data = json_decode($response, true);
var_dump($user_data);
7. Storing the Access Token for Later Use
- Unlike the authorization code, the access token’s lifetime is much longer. For Google, it is one hour, for GitHub, it is longer than that.
- So you can reuse it to verify the identity of the user during subsequent page visits or requests. The user need not go through the consent process everytime they try to access a protected resource on your application.
- The access token is usually stored in the user’s browser as a cookie, preferably as an HttpOnly cookie to prevent cross-site scripting attacks. The client application can then fetch that cookie and verify it’s validity.
- The cookie can also be persisted in a database in addition to storing it in the user’s browser. But that is not a necessity.
8. Renewing an expired Access Token
- After some time, the access token also expires. Right? Then what? Authenticate the user again? Not probably.
- I had earlier mentioned that a refresh token is also created along with the access token. The refresh token has much, much, much longer lifetime than the access token. Guess what? It never expires, and can be used to request a new access token when the old one expires.
- However, not all providers give a refresh token, especially if the access token has a long enough lifetime like months or years. Whereas in the case of Google, the lifetime of the access token is just one hour. After that, you need to request a new one with the refresh token.
- And once the refresh token is also expired, or if the user clears the browser cookies, then do the authentication again.
With that, I hope now you’ve got a basic understanding of what OAuth is, and how it works.