Blog Logo

9-Aug-2023 ~ 2 min read

Flask Google Signin


Table of contents

Introduction

Implementing Google OAuth2 Sign-In using REST HTTP endpoints without any libraries can be complex and involves multiple steps, including managing tokens, verifying signatures, and handling redirects. Here’s a simplified example of how you could implement Google OAuth2 Sign-In in Flask using REST HTTP endpoints for a home page and a profile page:

Setting Up Google API Credentials

Creating a Google API Project

Begin by creating a project in the Google API Console. This project will serve as the foundation for managing your application’s OAuth 2.0 credentials.

Configuring OAuth 2.0 Credentials

Within your Google API project, configure OAuth 2.0 credentials, including a client ID and client secret. These credentials enable your Flask application to authenticate users via Google.

Code

from flask import Flask, request, redirect, session, render_template
import requests
import base64
import hashlib
import os
import uuid

app = Flask(__name__)
app.secret_key = str(uuid.uuid4())

GOOGLE_AUTH_URL = 'https://accounts.google.com/o/oauth2/auth'
GOOGLE_TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'
GOOGLE_USERINFO_URL = 'https://www.googleapis.com/oauth2/v2/userinfo'

CLIENT_ID = "XXXXXXXXXXXX-XXXXh93sd784l51ic6to6q9f8ai4lto6.apps.googleusercontent.com"
CLIENT_SECRET = "XXXXXX-fWmjPyplP4qNGKbwYVyb1qx0HA0i"
SCOPE = 'openid%20email%20profile'
REDIRECT_URI = "http://localhost:5000/oauth2callback"


def generate_state():
    state = base64.urlsafe_b64encode(hashlib.sha256(os.urandom(1024)).digest()).decode('utf-8')
    session['state'] = state
    return state


@app.route('/')
def home():
    return render_template('home.html')


@app.route('/login')
def login():
    state = generate_state()
    auth_url = f'{GOOGLE_AUTH_URL}?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&response_type=code&scope={SCOPE}&state={state}'
    return redirect(auth_url)


@app.route('/oauth2callback')
def oauth2callback():
    if request.args.get('state') != session.get('state'):
        return 'Invalid state', 400

    authorization_code = request.args.get('code')

    token_payload = {
        'code': authorization_code,
        'client_id': CLIENT_ID,
        'client_secret': CLIENT_SECRET,
        'redirect_uri': REDIRECT_URI,
        'grant_type': 'authorization_code'
    }
    token_response = requests.post(GOOGLE_TOKEN_URL, data=token_payload)
    token_data = token_response.json()
    access_token = token_data.get('access_token')

    userinfo_response = requests.get(GOOGLE_USERINFO_URL, headers={'Authorization': f'Bearer {access_token}'})
    userinfo = userinfo_response.json()

    session['user'] = userinfo
    return redirect('/profile')


@app.route('/profile')
def profile():
    user = session.get('user')
    if not user:
        return redirect('/')
    return render_template('profile.html', user=user)


@app.route('/logout')
def logout():
    session.clear()
    return redirect('/')


if __name__ == '__main__':
    app.run()
<!-- templates/home.html -->
<!doctype html>
<html>
  <head>
    <title>Home</title>
  </head>
  <body>
    <h1>Welcome to My App</h1>
    <p><a href="/login">Sign in with Google</a></p>
  </body>
</html>
<!-- templates/profile.html -->
<!doctype html>
<html>
  <head>
    <title>Profile</title>
  </head>
  <body>
    <h1>Profile</h1>
    <p><strong>Name:</strong> {{ user.name }}</p>
    <p><strong>Email:</strong> {{ user.email }}</p>
    <p><a href="/">Home</a></p>
  </body>
</html>

Reference

Reference Google article: https://developers.google.com/identity/protocols/oauth2/web-server#httprest