Welcome folks today in this blog post we will be building the google oauth2 user login and logout
oauth2 system in browser using flask-login and sqlite
database. All the full source code of the application is shown below.
Get Started
In order to get started you need to create the requirements.txt
file inside the root directory and copy paste the following code
requirements.txt
1 2 3 4 5 |
requests==2.21.0 Flask==1.0.2 oauthlib==3.0.1 pyOpenSSL==19.0.0 Flask-Login==0.4.1 |
As you can see in the above file we have declared all the dependencies which is needed for this flask project. Now using the pip
command install these as shown below
pip install -r requirements.txt
Directory Structure
Now we need to create the schema.sql
file which will be the actual user data that we will be storing for this oauth2 google login and logout system
schema.sql
1 2 3 4 5 6 |
CREATE TABLE user ( id TEXT PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE NOT NULL, profile_pic TEXT NOT NULL ); |
As you can see we are creating the user table in which we have four fields namely the id
of the user which is the primary key, And then we have the name of the user, and then we have the email, and the profile picture as well.
Creating Database
Now we will be creating the database
file which will be db.py
here we will be creating the sqlite
db as shown below
db.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# http://flask.pocoo.org/docs/1.0/tutorial/database/ import sqlite3 import click from flask import current_app, g from flask.cli import with_appcontext def get_db(): if "db" not in g: g.db = sqlite3.connect( "sqlite_db", detect_types=sqlite3.PARSE_DECLTYPES ) g.db.row_factory = sqlite3.Row return g.db def close_db(e=None): db = g.pop("db", None) if db is not None: db.close() def init_db(): db = get_db() with current_app.open_resource("schema.sql") as f: db.executescript(f.read().decode("utf8")) @click.command("init-db") @with_appcontext def init_db_command(): """Clear the existing data and create new tables.""" init_db() click.echo("Initialized the database.") def init_app(app): app.teardown_appcontext(close_db) app.cli.add_command(init_db_command) |
As you can see it contains a variety of methods to get the db, and to create the database. So now guys we need to create the user model file called user.py
user.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
from flask_login import UserMixin from db import get_db class User(UserMixin): def __init__(self, id_, name, email, profile_pic): self.id = id_ self.name = name self.email = email self.profile_pic = profile_pic @staticmethod def get(user_id): db = get_db() user = db.execute( "SELECT * FROM user WHERE id = ?", (user_id,) ).fetchone() if not user: return None user = User( id_=user[0], name=user[1], email=user[2], profile_pic=user[3] ) return user @staticmethod def create(id_, name, email, profile_pic): db = get_db() db.execute( "INSERT INTO user (id, name, email, profile_pic)" " VALUES (?, ?, ?, ?)", (id_, name, email, profile_pic), ) db.commit() |
As you can see it contains the methods for inserting the user information directly into the user tables by using the INSERT Statement. And also if you want to select the information about any user. You can use the SELECT Statement.
Creating .env File
Now guys we want the client_id
and client_secret
from the google cloud console. Just copy paste the values inside the below code. Here we are creating the .env
file to store these values
.env
1 2 |
GOOGLE_CLIENT_ID=####yourclientid#### GOOGLE_CLIENT_SECRET=####yourclientsecret#### |
Now guys we want to create the app.py
file and copy paste the following code
app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# Python standard libraries import json import os import sqlite3 os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' # Third party libraries from flask import Flask, redirect, request, url_for from flask_login import ( LoginManager, current_user, login_required, login_user, logout_user, ) from oauthlib.oauth2 import WebApplicationClient import requests # Internal imports from db import init_db_command from user import User # Configuration GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID", None) GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET", None) GOOGLE_DISCOVERY_URL = ( "https://accounts.google.com/.well-known/openid-configuration" ) # Flask app setup app = Flask(__name__) app.secret_key = os.environ.get("SECRET_KEY") or os.urandom(24) if __name__ == "__main__": app.run(debug=True) |
As you can see we are importing all the required dependencies at the very top. And then we are initializing an OAuth2 config variables including the client_id and the client_secret from the environment variables. And then we are starting out the flask app at port 5000.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# User session management setup # https://flask-login.readthedocs.io/en/latest login_manager = LoginManager() login_manager.init_app(app) @login_manager.unauthorized_handler def unauthorized(): return "You must be logged in to access this content.", 403 # Naive database setup try: init_db_command() except sqlite3.OperationalError: # Assume it's already been created pass # OAuth2 client setup client = WebApplicationClient(GOOGLE_CLIENT_ID) # Flask-Login helper to retrieve a user from our db @login_manager.user_loader def load_user(user_id): return User.get(user_id) |
As you can see we are starting out the flask login manager passing the app object. And then we are initializing a new google oauth2
web client. And then inside the try block we are creating the sqlite
database whenever your application runs for the very first time. Now execute the app.py it will look something like this
python app.py
Now we need to create the index
route which will be the homepage of the application
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@app.route("/") def index(): if current_user.is_authenticated: return ( "<p>Hello, {}! You're logged in! Email: {}</p>" "<div><p>Google Profile Picture:</p>" '<img src="{}" alt="Google profile pic"></img></div>' '<a class="button" href="/logout">Logout</a>'.format( current_user.name, current_user.email, current_user.profile_pic ) ) else: return '<a class="button" href="/login">Google Login</a>' |
As you can see we are displaying a simple welcome message to the user if he or she is authenticated. The different details are shown such as the email and profile pic. And also we have the logout button. And if the user is not authenticated then we are showing the user with a anchor tag which says Google Login
as shown below
Getting Authorization Code Using OAuth2 Flow
Now we will redirect the user to the oauth2 redirect url
inside the browser when the user goes to the /login
route as shown below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@app.route("/login") def login(): # Find out what URL to hit for Google login google_provider_cfg = get_google_provider_cfg() authorization_endpoint = google_provider_cfg["authorization_endpoint"] # Use library to construct the request for login and provide # scopes that let you retrieve user's profile from Google request_uri = client.prepare_request_uri( authorization_endpoint, redirect_uri=request.base_url + "/callback", scope=["openid", "email", "profile"], ) print(request.base_url + "/callback") return redirect(request_uri) |
As you can see we are passing the authorization endpoint
and redirect_uri
of the project. And also we are providing the scopes
that we need to user access. Here we are providing the userprofile
and email
Getting Access Token From Authorization Code
Now guys we will be getting the access token from the authorization code to display the profile details of the user. For this we need to copy paste the following code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
@app.route("/login/callback") def callback(): # Get authorization code Google sent back to you code = request.args.get("code") # Find out what URL to hit to get tokens that allow you to ask for # things on behalf of a user google_provider_cfg = get_google_provider_cfg() token_endpoint = google_provider_cfg["token_endpoint"] # Prepare and send request to get tokens! Yay tokens! token_url, headers, body = client.prepare_token_request( token_endpoint, authorization_response=request.url, redirect_url=request.base_url, code=code, ) token_response = requests.post( token_url, headers=headers, data=body, auth=(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET), ) # Parse the tokens! client.parse_request_body_response(json.dumps(token_response.json())) # Now that we have tokens (yay) let's find and hit URL # from Google that gives you user's profile information, # including their Google Profile Image and Email userinfo_endpoint = google_provider_cfg["userinfo_endpoint"] uri, headers, body = client.add_token(userinfo_endpoint) userinfo_response = requests.get(uri, headers=headers, data=body) print(userinfo_response.json()) # We want to make sure their email is verified. # The user authenticated with Google, authorized our # app, and now we've verified their email through Google! if userinfo_response.json().get("email_verified"): unique_id = userinfo_response.json()["sub"] users_email = userinfo_response.json()["email"] picture = userinfo_response.json()["picture"] users_name = userinfo_response.json()["given_name"] else: return "User email not available or not verified by Google.", 400 # Create a user in our db with the information provided # by Google user = User( id_=unique_id, name=users_name, email=users_email, profile_pic=picture ) # Doesn't exist? Add to database if not User.get(unique_id): User.create(unique_id, users_name, users_email, picture) # Begin user session by logging the user in login_user(user) # Send user back to homepage return redirect(url_for("index")) def get_google_provider_cfg(): return requests.get(GOOGLE_DISCOVERY_URL).json() |
As you can see we are getting the user profile information and we are storing it inside the sqlite
database. And lastly we are redirecting the user to the index
page
Logout the User
Now guys we need to write the code to successfully logout the user as shown below.
1 2 3 4 5 |
@app.route("/logout") @login_required def logout(): logout_user() return redirect(url_for("index")) |
BUY FULL SOURCE CODE Using VISA CARD