Flask 整合 Google 登入(Google Sign-In)

Posted on  Apr 20, 2019  in  Flask  by  Amo Chen  ‐ 3 min read

隨著 Gmail 與 G Suite 的普及,越來越多人擁有 Google 的帳號,因此現代應用程式在整合登入服務時,支援使用 Google 帳號的進行登入也很重要,本篇記錄如何用 Flask 整合 Google Sign-In 。

本文環境

  • Python 3.6.5
  • Flask 1.0.2
  • Google API Python Client 1.7.8
  • Google Auth Oauthlib 0.2.0
$ pip install flask==1.0.2 google-api-python-client==1.7.8 google-auth-oauthlib==0.2.0

跟隨指引

前端整合 Google Sign-In 的步驟可以參考 Integrating Google Sign-In into your web app 一文。

主要是透過該文章提供的 CONFIGURE A PROJECT 按鈕新增一組 OAuth 2.0 Client ID (OAuth 2.0 用戶端 ID) 。

該組 ID 同樣可以在 Google Developers Console 中查到:

接著,為了整合方便,我們需要編輯該上述 OAuth 2.0 Client 的設定,加入 http://localhost:5000 (Flask 預設的通訊埠為 5000),使得我們可以在本機(localhost)執行 Google 所提供的 Javascript SDK 。

p.s. 請視情況加入你的域名清單至設定中

架設 Flask Server

取得 OAuth 2.0 Client ID 之後,就可以接著架設 Flask Server 進行後續的整合,以下是本文的 Flask 專案結構:

  .
  └── app/
       ├── app.py
       └── templates/
            └── index.html

app/ 資料夾內 app.py 是簡單的 Flask 程式,主要用來執行 Web server 並載入 templates/ 資料夾內的 index.html 以顯示 Google 登入頁給使用者。

app.py 內容如下(請將 your_oauth2_client_id 更換成你的 OAuth 2.0 Client ID ):

# -*- coding: utf-8 -*-
from google.oauth2 import id_token
from google.auth.transport import requests

from flask import Flask
from flask import request
from flask import jsonify
from flask import render_template


GOOGLE_OAUTH2_CLIENT_ID = 'your_oauth2_client_id'


def create_app(app_env=None):
    flask_app = Flask(__name__)
    return flask_app


app = create_app()


@app.route('/')
def index():
    return render_template('index.html', google_oauth2_client_id=GOOGLE_OAUTH2_CLIENT_ID)
    
    
@app.route('/google_sign_in', methods=['POST'])
def google_sign_in():
    token = request.json['id_token']
    
    try:
        # Specify the GOOGLE_OAUTH2_CLIENT_ID of the app that accesses the backend:
        id_info = id_token.verify_oauth2_token(
            token,
            requests.Request(),
            GOOGLE_OAUTH2_CLIENT_ID
        )

        if id_info['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
            raise ValueError('Wrong issuer.')

        # ID token is valid. Get the user's Google Account ID from the decoded token.
        # user_id = id_info['sub']
        # reference: https://developers.google.com/identity/sign-in/web/backend-auth
    except ValueError:
        # Invalid token
        raise ValueError('Invalid token')
 
    print('登入成功')
    return jsonify({}), 200

上述程式中值得說明的是 google_sign_in 會接收來自 index.html 所發出的 POST request ,並取得 request 中的 id_token 資料。接著利用 Google 所提供的官方 OAuth2 套件驗證該 id_token 是否為 Google 所發出的 token ,如果是正確的 token 就能夠取得使用者的 Google 資料( email, ID, Name 等使用者所授權的資料)。至此我們可以將這些資料依照需求自行儲存至資料庫中,並視為使用者完成註冊或登入(本文就不多加示範這段過程囉!)。

p.s. 上述程式中的 id_info 存有哪些資料可以參閱 https://developers.google.com/identity/sign-in/web/backend-auth

index.html 內容如下:

<html>
  <head>
    <title>Google Sign-In</title>
    <meta name="google-signin-client_id" content="{{ google_oauth2_client_id }}">
    <script src="https://apis.google.com/js/platform.js" async defer></script>
    <script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
  </head>
  <body>
    <script>
      function onSignIn(googleUser) {
        var id_token = googleUser.getAuthResponse().id_token;
        var profile = googleUser.getBasicProfile();
        console.log('ID: ' + profile.getId()); // Do not send to your backend! Use an ID token instead.
        console.log('Name: ' + profile.getName());
        console.log('Image URL: ' + profile.getImageUrl());
        console.log('Email: ' + profile.getEmail()); // This is null if the 'email' scope is not present.
        $.ajax({
          type: "POST",
          url: '/google_sign_in',
          data: JSON.stringify({'id_token': id_token}),
          success: function() {
            console.log('login success')
          },
          dataType: 'application/json',
          contentType:"application/json",
        });
      }
    </script>
    <div class="g-signin2" data-onsuccess="onSignIn"></div>
  </body>
</html>

上述範例程式則相對簡單,載入 https://apis.google.com/js/platform.js Google 所提供的 SDK 之後,並加入 <div class="g-signin2" data-onsuccess="onSignIn"></div> 讓 Google 的 SDK 自行在該 HTML 元素加入 Google 登入按鈕。接著實作 onSignIn 函式,該函式會在使用者授權登入後執行,因此可以透過 googleUser.getAuthResponse().id_token 取得使用者的 id_token ,並送至後端伺服器的 API 完成後續的登入流程。

值得注意的是 Google 推薦前端只傳送 id_token 至後端伺服器的 API 即可,這是因為透過後端驗證 id_token 的正確性之後,再利用該 id_token 取得使用者的相關資料是最安全的,如此可避免有人偽造資料。

最後,執行以下指令試試看是否能夠透過 Google Sign-In 吧!

$ env FLASK_APP=app.py flask run

接著打開 http://localhost:5000 正確執行的話就可以看到首頁出現 Google 登入按鈕。

按下登入按鈕後 Flask Server 則列印登入成功文字!

以上就是 Flask 如何整合 Google Sign-In 的紀錄! Happy Coding!

Reference

https://developers.google.com/identity/sign-in/web/sign-in

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

追蹤新知

看完這篇文章了嗎?還意猶未盡的話,追蹤粉絲專頁吧!

我們每天至少分享 1 篇文章/新聞或者實用的軟體/工具,讓你輕鬆增廣見聞提升專業能力!如果你喜歡我們的文章,或是想了解更多特定主題的教學,歡迎到我們的粉絲專頁按讚、留言讓我們知道。你的鼓勵,是我們的原力!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!