Skip to main content

Command Palette

Search for a command to run...

How to Create a Google Login in Flask

Updated
10 min read
How to Create a Google Login in Flask
J

I am Data Engineer with a big passion for learning as much as he can. I enjoy the outdoors, mountain biking, finding cool ways to solve new coding problems, and teaching others to code.

Creating a mechanism for login is probably about one of the first things you should think about when creating an application. When creating a login there’s always the option of utilizing pre-built solutions. However, what if you need the flexibility that comes with building your own login?

When building your own, you could go through the process of creating usernames, managing passwords, MFA, recovery, among other things. This becomes incredibly arduous (which explains the popularity of the pre-built solutions). That being said, there’s always the option to build a login via a service like Google that already has security standards built in - and let’s be honest, almost everyone has a Google account.

Implementing a Google driven login isn’t all that involved, so let’s walk through how to do it within a Flask app!

Requirements

Before we get started, let’s go through the requirements you’ll need to build a Google login via Flask:

  • IDE - your choice of an integrated development environment (VSCode, Jetbrains, etc.)

  • Python - I’m utilizing 3.12, but just about any version after 3.6 should work fine - but you’ll also need the following modules:

    • Flask python -m pip install flask

    • Google Auth python -m pip install google-auth

    • Google Auth Oauthlib python -m pip install google-auth-oauthlib

    • Dotenv (Optional) python -m pip install python-dotenv - this is for setting up our .env files and storing our configuration/secrets versus storing it in code

  • Google OAuth Client - this is a unique client/application within the Google environment (Google Cloud), I’ll go into more depth in the next section on how to set this up

Google OAuth Client

One of the biggest requirements you’ll need is a Google OAuth client id. Good thing, these are free and pretty straightforward to set up. You can utilize Google’s directions here.

However, if you don’t want to utilize Google’s directions for whatever reason, you can follow mine below!

First navigate to Google Cloud console. If you don’t have an account already, you’ll be asked to create one. From here, traditionally you would navigate to the “API and Services” section, however, Google has recently updated that experience as writing this, so navigate to the “Google Auth Platform”. From here, it will likely ask you to create a new project:

From here, navigate to the “Clients” section, and click “Get Started”:

You’ll then have to go through the process of setting it up - you’ll be asked to provide a name for the client, contact information, audience (whether the app will be external/internal):

After this, you can then navigate to the “Clients” menu and create the client:

Configuration of the client is relatively straightforward. The biggest thing you might need to adjust is the redirect URIs. Which we’ll update later in the post, but for now, we’ll just leave it empty:

The final bit of configuration is the fact we’ll need to set up the scopes of our client. A scope is essentially what information your client will be able to access when a user logs in. So navigate to “Data Access”, then set up scopes - You’ll likely just need a few scopes (but can always add more later):

  • userinfo.email

  • userinfo.profile

  • openid

Once everything is configured, navigate back to your client, then we’re going to take note of the “Client ID” and “Client secret”. We will need both of these for our Flask application to support login:

That’s it! Our client is set up - we’ll make some more adjustments later, but let’s go ahead and get started building our Flask app.

Coding our App

We’re going to create everything in one file for this post, but just know you’ll typically want to break these things out into separate Flask blueprints and maybe code specific methods to handle different parts of the login process.

Base Flask App

First, let’s go ahead and create the basic Flask app in our app.py file.

from flask import Flask

app = Flask(__name__)

@app.route("/")
def app_root():
    return "API is running", 200

if __name__ == '__main__':    
    app.run(
        port=10000,
        debug=True,
    )

We’re going to ensure our app runs on port 10000, and debug=True.

From here, we’re going to create the main/root route for our app. We’re going to create a login button, that redirects to our Google login (/google_login).

@app.route("/")
def app_root():
    return """  
        <button onclick="window.location.href='/google_login'">
            Login with Google
        </button>
    """, 200

Next let’s create our “/google_login” route:

@app.route("/google_login")
def google_login():
    return "google_login", 200

Once coded, try running the flask app, and you should see the “Login with Google” button on the root of your application! Next we’ll work on coding out the login routes!

Google Login Route

Required Modules

Next we’re going to work on fleshing out our Google login route.

To login via Google, we’re going to require several different modules:

  • request, session, and redirect from flask

  • os

  • Flow from google_auth_oauthlib.flow

  • id_token from google.oauth2

  • requests from google.auth.transport

  • (optional) dotenv

You can also update the top of your app.py file to look something like:

from flask import Flask
from flask import request, session, redirect

import dotenv
import os
from google_auth_oauthlib.flow import Flow
from google.oauth2 import id_token
from google.auth.transport import requests as google_auth_requests

App Secret Key + OAUTH Transport

For our app to work with Google login, we’ll need to add a “secret_key” to the app, and set “OAUTHLIB_INSECURE_TRANSPORT” to 1.

  • app.secret_key is a way each session is signed securely within the application - we’re going to set it to a random value

  • OAUTHLIB_INSECURE_TRANSPORT - OAUTH requires SSL transport - however, while testing (generally), you don’t have SSL setup - so this bypasses that requirement, and allows you to test during development over http

    • When you FINALIZE your app, you’ll most definitely want to set up SSL to keep everything secure
app = Flask(__name__)
#setting app secret key
app.secret_key = os.urandom(24) 
#setting OAUTHLIB insecure transport to 1
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'

Client Secret + ID

Next, I’m going to add my Google client id + secret to a .env file as well, so that I can import them utilizing dotenv.

.env file contents

GOOGLE_CLIENT_ID=client_id
GOOGLE_CLIENT_SECRET=client_secret

Importing .env file

app = Flask(__name__)
#setting app secret key
app.secret_key = os.urandom(24) 
#setting OAUTHLIB insecure transport to 1
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'

Google Login Route

Finally, we’re going to update the “/google_login” route to manage the login flow:

@app.route("/google_login")
def google_login():
    #creates google login flow object
    flow = Flow.from_client_config(
        client_config={
            "web":
            {
                "client_id":os.environ.get("GOOGLE_CLIENT_ID")
                ,"client_secret":os.environ.get("GOOGLE_CLIENT_SECRET")
                ,"auth_uri":"https://accounts.google.com/o/oauth2/v2/auth"
                ,"token_uri":"https://oauth2.googleapis.com/token"
            }
        }
        #if you need additional scopes, add them here
        ,scopes=[
            "https://www.googleapis.com/auth/userinfo.email"
            ,"https://www.googleapis.com/auth/userinfo.profile"
            ,"openid"
        ]      
    )      

    #redirect uri for the google callback (i.e., the route in our api that handles everything AFTER google auth)
    flow.redirect_uri = "http://localhost:10000/google_login/oauth2callback"

    #pulling authorization url (google login), and state to store in Flask session
    authorization_url, state = (
        flow.authorization_url(
            access_type="offline"
            ,prompt="select_account"
            ,include_granted_scopes="true"
        )
    )

    #connecting/storing state and final redirect AFTER login in the Flask API
    session['state'] = state
    session['final_redirect'] = "http://localhost:10000/logged_in"

    #redirecting to the authorization URL
    return redirect(authorization_url)

After this is configured, run the Flask app. Navigate to your Flask app root (http://localhost:10000):

Click the login button, and it should redirect you to a page like this:

Update your Google Client with Redirect URI

The last error means we need to configure our Google client to allow redirect back to a specific uri (i.e., the redirect_uri we just configured when setting up our route http://localhost:10000/google_login/oauth2callback).

So we’ll navigate back to our Google client and add this uri to our “Authorized redirect URIs”. This is allowing Google to redirect back to a specific url once a user is authenticated. This keeps your app secure, and prevents redirects to unknown urls.

When your app goes to production, this URI will look much different, but for now, this focuses on our local development environment.

Now go back through your login, and you should get the following screen:

You can then choose to sign in, and will be asked to “Continue” or “Cancel”. This screen is asking whether or not you give the specific application/client to access your information from Google. Once you hit continue, you’ll be hit with a “Not Found” error. This simply happens because we haven’t created our callback endpoint yet, but we’ll create that next!

Auth Callback Route

The callback endpoint handles everything AFTER someone authenticates via Google. Long short, Google authenticates a user, then passes them back to your specific callback endpoint - where you need to do some additional validation/work.

The biggest thing we’ll want to accomplish with this callback endpoint is validate the user that is coming to us is actually authenticated and real. As well, we’ll want to complete our final redirect to our logged in page!

@app.route("/google_login/oauth2callback")
def auth_login_google_oauth2callback():
    #pull the state from the session
    session_state = session['state']
    redirect_uri = request.base_url
    #pull the authorization response
    authorization_response = request.url  
    #create our flow object similar to our initial login with the added "state" information
    flow = flow = Flow.from_client_config(
        client_config={
            "web":
            {
                "client_id":os.environ.get("GOOGLE_CLIENT_ID")
                ,"client_secret":os.environ.get("GOOGLE_CLIENT_SECRET")
                ,"auth_uri":"https://accounts.google.com/o/oauth2/v2/auth"
                ,"token_uri":"https://oauth2.googleapis.com/token"
            }
        }
        ,scopes=[
            "https://www.googleapis.com/auth/userinfo.email"
            ,"https://www.googleapis.com/auth/userinfo.profile"
            ,"openid"
        ]  
        ,state=session_state    
    )  

    flow.redirect_uri = redirect_uri  
    #fetch token
    flow.fetch_token(authorization_response=authorization_response)
    #get credentials
    credentials = flow.credentials
    #verify token, while also retrieving information about the user
    id_info = id_token.verify_oauth2_token(
        id_token=credentials._id_token
        ,request=google_auth_requests.Request()
        ,audience=os.environ.get("GOOGLE_CLIENT_ID")
    )    
    #setting the user information to an element of the session
    #you'll generally want to do something else with this (login, store in JWT, etc)
    session["id_info"] = id_info

    #redirecting to the final redirect (i.e., logged in page)
    redirect_response = redirect(session['final_redirect'])   

    return redirect_response

Logged In Route

Finally, once everything is coded, we’ll create our login route. This will display basic information about the user that just logged in:

@app.route("/logged_in")
def logged_in():
    #retrieve the users picture
    picture = session["id_info"]["picture"]
    #retrieve the users email
    email = session["id_info"]["email"]

    #render the email/picture
    return f"""
    <h1>Logged In</h1>
    <p>Email: {email}</p>
    <img src="{picture}" />
    """, 200

Once that’s coded, run the app, going through the login process (starting at localhost:10000) and you should end up seeing a page like this:

That’s it! You now have a basic Google login built in flask!

Other Considerations

This is a VERY simple example, and your requirements might be varied, so some of the biggest considerations when implementing a google authentication/logic process comes down to security and do you want to utilize pre-built solutions.

Security

One of the biggest considerations is security. While this example is super simple, your needs/requirements might be varied. Depending on how you need/want to implement this, you might need to implement a database to store user information, create other authentication/security mechanisms (e.g., JWT, CSRF). Each different requirement might require a different solution or security measure, simply because the last thing you want when instituting authentication for your app is to have a gap in security.

When I was first wanting to implement a Google login, with every new requirement and layer to my application, I had new considerations for security. It can become a lot, namely if you’re wanting to implement something relatively simple.

This leads me into the next consideration.

Pre-built Solutions

I think it goes without saying, but there is nothing wrong with utilizing pre-built solutions. Just like utilizing modules in python, you don’t want to have to build everything. So, based on your requirements, do some research into solutions that you can utilize for authentication.

That being said, there is a tremendous benefit to building something on your own, even if it isn’t something that never leaves local development. Knowing how some of these things work will only sharpen your skills when coding solutions in the future! Just make sure you consider all angles and needs prior to building it on your own!

Conclusion

Utilizing Google as a login method for your application is relatively straightforward (security requirements withstanding), with pre-built security it’s a better solution that building a completely homegrown solution. That being said, as with every project, there are always different requirements and needs, so make sure you evaluate all requirements and proposed solutions appropriately!

Happy coding!