from Guide to Hacking on Jun 4, 2023
How third-party login works
At some point, you've likely logged into a web app using a single sign on — such as Google, Facebook, or Twitter. As a user, the process is simple enough, but as a developer, third-party signin can be a pain to understand and implement.
Fortunately, there are a large number of tools that can help facilitate third-party login more smoothly. Almost all such systems follow the same flow, so the flow you'll learn generalizes across platforms.
In this post, we'll briefly explain how third-party login works and build a super short demo application supporting Google login.
How to login with a third party
Let's start with the simplest flow. Say you're trying to sign into Slack using your Google account. In this scenario, you stay entirely within your browser, as you're using the Slack web app. There are two critical requirements that need to be satisfied, among others:
- Identify the app: Slack must include information that identifies itself, when sending you to Google's signin page.
- Receive authorization token: Google must somehow send a special token back to the Slack web app.
To start, Slack must register with Google on the developer console, so that Slack knows how to identify itself when communicating with Google. Then, when you login, Slack follows this procedure:
- You click "login" on
slack.com
. This satisfies requirement #1. At this point, Slack builds a Google login URL that includes information about the requestor — i.e., Slack. - Your browser navigates you to
accounts.google.com/signin
. - You login normally.
- You grant permission to Slack to access your information.
- You are redirected back to
slack.com
. This satisfies requirement #2. The redirect back to Slack includes the special token. - Slack now uses that special token to ask Google for your information, such as your name and email address.
This process is relatively straightforward. Let's now follow this process ourselves for a demo application.
Register with Google
Navigate to the OAuth consent screen page in the Google developers console. Follow the onscreen prompts to setup your application:
-
Select External. Hit Save and continue.
- You could select either Internal or External, but most users will only have the External option available. There's no difference while we're just testing the application.
-
OAuth consent screen: Fill in your App name, User support email, and Developer contact information. The remaining fields are optional for now. Hit Save and continue.
-
Scopes: Click on Add or Remove Scopes, and in the popup, select the first three scopes
.../auth/userinfo.email
,.../auth/userinfo.profile
, andopenid
. Click Update to close the dialog. Hit Save and continue.- These three scopes grant the application access to basic user information that we'll use for our demo application.
-
Test users: Select Add Users, type in your own email address, and click Add to close the dialog. Hit Save and continue.
- This ensures that you can test your own application.
-
Summary: Click Back to dashboard.
Next, click on the "Credentials" tab on the left. Alternatively, click here to navigate to the credentials page in the Google developers console. You'll see a webpage similar to the following.
Click on + Create Credentials at the top, and select OAuth Client ID.
Follow the onscreen prompts to fill out the form.
- Under Application type, select Web application.
- Change the Name if you'd like. I've left the default name "Web client 1".
- Under Authorized redirect URIs, click Add URI.
- This creates a new field. In that field, fill in the URI
https://127.0.0.1:5000/login/callback
. - Click out of the field and hit Create.
You should now see a popup that says OAuth client created. In that popup, click Download JSON to download a client_secrets.json
file. You can now close the popup by clicking OK.
We have now successfully registered with Google. You may now close your Google cloud console.
Setup your environment
If you know your way around a clean file system and Python's virtual environment, then feel free to skip this step and use your own system. However, if you're unfamiliar with those ideas, then I recommend following these steps to save yourself headache in the future.
To get started, create a folder for your project. We'll create a folder on our Desktop as an example.
mkdir ~/Desktop/thirdpartylogin
cd ~/Desktop/thirdpartylogin
In your new directory, create a virtual environment and activate it, to house your project-specific dependencies.
python -m venv env
source env/bin/activate
You're now ready to proceed.
Build a Google login flow
Move your client_secrets.json
file to your current folder. The below command assumes you've downloaded the client secrets file to ~/Downloads
.
mv ~/Downloads/client_secrets.json .
Install a few prerequisite libraries:
- Flask for our web framework
- cryptography to provide support for accessing our web application via
https
- Simple Flask Google Login for an easy, out-of-the-box implementation of Google login using Flask
pip install Flask==2.3.2 cryptography==41.0.1 simple-flask-google-login==0.3.0
Then, write the following to server.py
, also in your current folder, which is starter code for your Google login web app.
from flask import Flask, session
from simple_flask_google_login import SimpleFlaskGoogleLogin
app = Flask("Google Login App")
app.secret_key = "YourSecretKeyHere" # Secret key is needed for OAuth 2.0
SimpleFlaskGoogleLogin(app)
@app.route("/")
def index():
if 'name' in session:
return f"Hello {session['name']}! <a href='/logout'>Logout</a>"
return "<a href='/login'>Login</a>"
if __name__ == "__main__":
app.run(ssl_context='adhoc', debug=True)
Optionally, check your source code against our reference implementation for this "hello world" step. Run this with python server.py
. Once the server has launched,
- Open https://127.0.0.1:5000 in your browser.
- Click on "Login".
- Your browser navigates you to
accounts.google.com/signin
. - Login to Google normally.
- You are redirected back to your application at https://127.0.0.1:5000.
- You should now see a webpage that shows "Hello
!".
With that, your Google login flow has been completed!
This is an example of a successful login, shown below.
Moving forward, here are some pointers on customizing the login flow to your liking:
- Need additional scopes? If your application accesses additional information like calendar events or email, then you'll need additional "scopes". To declare additional scopes that your application uses, add the scopes in the OAuth consent screen page and additionally pass the scope URIs to
SimpleFlaskGoogleLogin(scopes=...)
. - Custom URLs? By default, the application above uses the
/login
URL to redirect the user to Google. You can askSimpleFlaskGoogleLogin
to use a different URL for its login redirect URL withFlaskGoogleLogin().init_app(app, login_endpoint="/login/redirect")
, freeing up the/login
URL for you to customize. Note that in this example, your custom/login
page then needs to send users to/login/redirect
.
You can learn more from the Github repository README for Simple Flask Google Login.
Build a flow from scratch
Now that we've seen a minimal working example, let's rebuild that minimal working example from scratch.
Starting from our minimal example above, remove the helper on Line 7, SimpleFlaskGoogleLogin(app)
.
Add a flow object from Google's official oauth library. This will handle the oauth2 exchange for us.
cache = {}
flow = Flow.from_client_secrets_file(
client_secrets_file='./client_secrets.json',
scopes=["https://www.googleapis.com/auth/userinfo.email", "openid", "https://www.googleapis.com/auth/userinfo.profile"],
redirect_uri='https://127.0.0.1:5000/login/callback',
)
We now need to integrate this flow object with Flask. Add the login endpoint. This will redirect the user to the Google login page.
@app.route('/login')
def login():
return redirect(flow.authorization_url()[0])
Add the callback endpoint. This will handle the Google login code and exchange that code for an authorization token. This token can then be used to ask Google for information about the user.
@app.route('/login/callback')
def callback():
flow.fetch_token(authorization_response=request.url)
google = flow.authorized_session()
user = google.get(URL_USERINFO).json()
cache.update(user)
return redirect('/')
Finally, add a logout endpoint. This will logout the user by clearing the session.
@app.route('/logout')
def logout():
cache.clear()
return redirect('/')
And that's it! Check your source code against our reference implementation for this "manual" step. Launch your application.
python server.py
Click Login just as before, and follow the onscreen prompts. You should end up with a successful login once more.
Note that this version is a slightly watered-down version of the SimpleFlaskGoogleLogin
but it covers the major parts of an oauth flow.
Conclusion
This concludes the quick tutorial on third-party logins. With the script above, you should have enough boilerplate to start implementing Google logins in a jiffy.
For reference, here is the Simple Flask Google Login library we used for our boilerplate code. Bookmark or star that repository to get up and running quickly in the future.
You can find the source code for this tutorial, as well as the finished code, on my "Guide to Hacking" repository.
Want more tips? Drop your email, and I'll keep you in the loop.