Recently, I integrated SSO with Google Sign-In into a web application using the oauthlib library. While working on this project, I found plenty of resources on SSO and oauthlib separately, but struggled to find a tutorial that could guide me through the setup of Google Sign-In using oauthlib in a timely manner. Therefore, I’ve written this post to provide a quick guide for those who want to implement the Authorization Code flow for Google Sign-In in Flask applications.
Would you benefit from reading this post?
Please note that this post assumes a certain level of familiarity with the topic at hand. While I won’t be providing an introduction to the basics, I trust that those who are already knowledgeable in this area will find the content useful:
- OAauth2 workflow
- how to generate CLIENT ID, CLIENT SECRET & a Authorized redirect URI from Google developer console
- list of permissions that your application requires(scope)
After completing this tutorial, it will be possible for you to integrate a Google sign-in page into your application. The resulting workflow would resemble the following gif
Implement Google sign-in:
To follow the steps in this section, you’ll need to have already generated a CLIENT_ID
, CLIENT_SECRET
, and redirect_uri
, and have decided on the resource
(in our case, email) that you’ll be requesting for your application. Once you have these details, implementing Google Sign-In in a Flask app is just a matter of two simple steps:
- Generate a Google Sign-In URI, route the users to this Sign-In page from your landing page
- Redirect the user to a success page after they have successfully logged in using their Google credentials
1. Generate a Google Sign-In URI, route the users to this Sign-In page from your landing page
Create a landing page like the above image with Google Sign-In URI using oauthlib
, this login page will prompt the users to enter their Google username and password.
CLIENT_ID = os.environ['CLIENT_ID'] CLIENT_SECRET = os.environ['CLIENT_SECRET'] DATA = { 'response_type':"code", # this tells the auth server that we are invoking authorization workflow 'redirect_uri':"https://localhost:5000/home", # redirect URI https://console.developers.google.com/apis/credentials 'scope': 'https://www.googleapis.com/auth/userinfo.email', # resource we are trying to access through Google API 'client_id':CLIENT_ID, # client ID from https://console.developers.google.com/apis/credentials 'prompt':'consent'} # adds a consent screen URL_DICT = { 'google_oauth' : 'https://accounts.google.com/o/oauth2/v2/auth', # Google OAuth URI 'token_gen' : 'https://oauth2.googleapis.com/token', # URI to generate token to access Google API 'get_user_info' : 'https://www.googleapis.com/oauth2/v3/userinfo' # URI to get the user info } # Create a Sign in URI CLIENT = oauth2.WebApplicationClient(CLIENT_ID) REQ_URI = CLIENT.prepare_request_uri( uri=URL_DICT['google_oauth'], redirect_uri=DATA['redirect_uri'], scope=DATA['scope'], prompt=DATA['prompt']) |
Lets create a Flask view function to land the users on the Sign-In page
@app.route('/') def login(): "Home" # redirect to the newly created Sign-In URI return redirect(REQ_URI) |
2. Redirect to success page after successfully logging in using Google credentials
Lets create a home route function that implements a page like the above image that redirects to redirect_uri set when generating CLIENT_ID and CLIENT_SECRET
@app.route('/home') def home(): "Redirect after Google login & consent" # Get the code after authenticating from the URL code = request.args.get('code') # Generate URL to generate token token_url, headers, body = CLIENT.prepare_token_request( URL_DICT['token_gen'], authorisation_response=request.url, # request.base_url is same as DATA['redirect_uri'] redirect_url=request.base_url, code=code) # Generate token to access Google API token_response = requests.post( token_url, headers=headers, data=body, auth=(CLIENT_ID, CLIENT_SECRET)) # Parse the token response CLIENT.parse_request_body_response(json.dumps(token_response.json())) # Add token to the Google endpoint to get the user info # oauthlib uses the token parsed in the previous step uri, headers, body = CLIENT.add_token(URL_DICT['get_user_info']) # Get the user info response_user_info = requests.get(uri, headers=headers, data=body) info = response_user_info.json() return redirect('/user/%s' % info['email']) |
Lets create a login_success function to redirect the user to a dynamic page with the user’s email
@app.route('/user/<email>') def login_success(email): "Landing page after successful login" return "Hello %s" % email |
Putting it all together
#!/usr/bin/env python3 """ A Flask app for Google SSO """ import json import os import requests from flask import Flask, redirect, request from oauthlib import oauth2 CLIENT_ID = os.environ['CLIENT_ID'] CLIENT_SECRET = os.environ['CLIENT_SECRET'] DATA = { 'response_type':"code", # this tells the auth server that we are invoking authorization workflow 'redirect_uri':"https://localhost:5000/home", # redirect URI https://console.developers.google.com/apis/credentials 'scope': 'https://www.googleapis.com/auth/userinfo.email', # resource we are trying to access through Google API 'client_id':CLIENT_ID, # client ID from https://console.developers.google.com/apis/credentials 'prompt':'consent'} # adds a consent screen URL_DICT = { 'google_oauth' : 'https://accounts.google.com/o/oauth2/v2/auth', # Google OAuth URI 'token_gen' : 'https://oauth2.googleapis.com/token', # URI to generate token to access Google API 'get_user_info' : 'https://www.googleapis.com/oauth2/v3/userinfo' # URI to get the user info } # Create a Sign in URI CLIENT = oauth2.WebApplicationClient(CLIENT_ID) REQ_URI = CLIENT.prepare_request_uri( uri=URL_DICT['google_oauth'], redirect_uri=DATA['redirect_uri'], scope=DATA['scope'], prompt=DATA['prompt']) app = Flask(__name__) @app.route('/') def login(): "Home" # redirect to the newly created Sign-In URI return redirect(REQ_URI) @app.route('/home') def home(): "Redirect after Google login & consent" # Get the code after authenticating from the URL code = request.args.get('code') # Generate URL to generate token token_url, headers, body = CLIENT.prepare_token_request( URL_DICT['token_gen'], authorisation_response=request.url, # request.base_url is same as DATA['redirect_uri'] redirect_url=request.base_url, code=code) # Generate token to access Google API token_response = requests.post( token_url, headers=headers, data=body, auth=(CLIENT_ID, CLIENT_SECRET)) # Parse the token response CLIENT.parse_request_body_response(json.dumps(token_response.json())) # Add token to the Google endpoint to get the user info # oauthlib uses the token parsed in the previous step uri, headers, body = CLIENT.add_token(URL_DICT['get_user_info']) # Get the user info response_user_info = requests.get(uri, headers=headers, data=body) info = response_user_info.json() return redirect('/user/%s' % info['email']) @app.route('/user/<email>') def login_success(email): "Landing page after successful login" return "Hello %s" % email if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5000, ssl_context='adhoc') |
To view this project on GitHub, simply follow this link: sso_google_oauthlib
That’s it! With this tutorial, you can easily integrate a Google Sign-In page into your Flask application
Hire Qxf2 for your testing needs
Modern stacks need modern testing. Contact Qxf2 to work with expert technical testers. Our testers go well beyond traditional test automation. We can help your teams test infrastructure maintained as code, add several types of tests to your data pipelines, integrate deep testing into your MLOPs cycle and so much more!
My expertise lies in engineering high-quality software. I began my career as a manual tester at Cognizant Technology Solutions, where I worked on a healthcare project. However, due to personal reasons, I eventually left CTS and tried my hand at freelancing as a trainer. During this time, I mentored aspiring engineers on employability skills. As a hobby, I enjoyed exploring various applications and always sought out testing jobs that offered a good balance of exploratory, scripted, and automated testing.
In 2015, I joined Qxf2 and was introduced to Python, my first programming language. Over the years, I have also had the opportunity to learn other languages like JavaScript, Shell scripting (if it can be called a language at all), and more recently, Rust. Despite this exposure, Python remains my favorite language due to its simplicity and the extensive support it offers for libraries.
In my free time, I like to watch football (I support Arsenal Football Club), play football myself, and read books.
Thank you, great tutorial and this method makes the Google consent screen load faster.
Sorry, it didn’t work on my end.
Can you please post the issue you have hit?
there is ssl issue, once authorisation completes it gives ERR_SSL_PROTOCOL_ERROR
Hi,
I am reading there could be a lot of reasons why
ERR_SSL_PROTOCOL_ERROR
is triggered. Can you please share more info on the error:– Did you try using a different browser and do you see the same error?
– Do you have the `
ssl_context='adhoc'
param set when callingapp.run
method?