Memigrasikan App Identity ke Token ID OIDC

Saat aplikasi yang berjalan di runtime Python 2 mengirim permintaan ke aplikasi App Engine lain, aplikasi tersebut dapat menggunakan App Identity API App Engine untuk menyatakan identitasnya. Aplikasi yang menerima permintaan tersebut dapat menggunakan identitas ini untuk menentukan apakah aplikasi harus memproses permintaan tersebut atau tidak.

Jika aplikasi Python 3 Anda perlu menyatakan identitasnya saat mengirim permintaan ke aplikasi App Engine lain, Anda dapat menggunakan token ID OpenID Connect (OIDC) yang diterbitkan dan didekode oleh OAuth 2.0 API Google.

Berikut adalah ringkasan penggunaan token ID OIDC untuk menyatakan dan memverifikasi identitas:

  1. Aplikasi App Engine yang bernama "Aplikasi A" mengambil token ID dari lingkungan runtime Google Cloud.
  2. Aplikasi A menambahkan token ini ke header permintaan tepat sebelum mengirim permintaan ke Aplikasi B, yang merupakan aplikasi App Engine lainnya.
  3. Aplikasi B menggunakan OAuth 2.0 API Google untuk memverifikasi payload token. Payload yang didekode berisi identitas terverifikasi Aplikasi A dalam bentuk alamat email akun layanan default Aplikasi A.
  4. Aplikasi B membandingkan identitas dalam payload dengan daftar identitas yang dapat direspons. Jika permintaan berasal dari aplikasi yang diizinkan, Aplikasi B akan memproses permintaan dan merespons.

Proses OAuth 2.0

Panduan ini menjelaskan cara memperbarui aplikasi App Engine agar menggunakan token ID OpenID Connect (OIDC) untuk menyatakan identitas, dan memperbarui aplikasi App Engine lainnya untuk menggunakan token ID untuk memverifikasi identitas sebelum memproses permintaan.

Perbedaan utama antara App Identity dan OIDC API

  • Aplikasi di runtime Python 2 tidak perlu menyatakan identitas secara eksplisit. Jika aplikasi menggunakan library Python httplib, urllib, atau urllib2 atau layanan URL-fetch App Engine untuk mengirim permintaan keluar, runtime akan menggunakan layanan URL-fetch App Engine untuk membuat permintaan. Jika permintaan dikirim ke domain appspot.com, Fetch URL otomatis akan menanyakan identitas aplikasi yang meminta dengan menambahkan header X-Appengine-Inbound-Appid ke permintaan tersebut. Header tersebut berisi ID aplikasi (juga disebut project ID).

    Aplikasi di runtime Python 3 secara eksplisit perlu menyatakan identitas dengan mengambil token ID OIDC dari lingkungan runtime Google Cloud dan menambahkannya ke header permintaan. Anda harus memperbarui semua kode yang mengirim permintaan ke aplikasi App Engine lain agar permintaan tersebut berisi token ID OIDC.

  • Header X-Appengine-Inbound-Appid dalam permintaan berisi project ID aplikasi yang mengirim permintaan.

    Payload token ID OIDC Google tidak secara langsung mengidentifikasi project ID aplikasi itu sendiri. Sebagai gantinya, token mengidentifikasi akun layanan tempat aplikasi berjalan, dengan memberikan alamat email akun layanan tersebut. Anda harus menambahkan beberapa kode untuk mengekstrak nama pengguna dari payload token.

    Jika akun layanan tersebut adalah akun layanan App Engine default tingkat aplikasi untuk project, project ID dapat ditemukan di alamat email akun layanan tersebut. Bagian nama pengguna pada alamat sama dengan project ID. Dalam hal ini, kode aplikasi penerima dapat mencarinya dalam daftar project ID yang akan mengizinkan permintaan darinya.

    Namun, jika aplikasi yang meminta menggunakan akun layanan yang dikelola pengguna, bukan akun layanan App Engine default, maka aplikasi penerima hanya dapat memverifikasi identitas akun layanan tersebut, yang tidak serta merta menentukan project ID aplikasi yang meminta. Dalam hal ini, aplikasi penerima harus mengelola daftar email akun layanan yang diizinkan, bukan daftar project ID yang diizinkan.

  • Kuota untuk panggilan URL Fetch API berbeda dengan kuota OAuth 2.0 API Google untuk memberikan token. Anda dapat melihat jumlah token maksimum yang dapat diberikan per hari di layar izin OAuth Konsol Google Cloud. URL Fetch, App Identity API, maupun OAuth 2.0 API Google tidak akan dikenai biaya.

Ringkasan proses migrasi

Untuk memigrasikan aplikasi Python Anda agar menggunakan OIDC API untuk menyatakan dan memverifikasi identitas:

  1. Di aplikasi yang perlu menyatakan identitas saat mengirim permintaan ke aplikasi App Engine lainnya:

    1. Tunggu hingga aplikasi Anda berjalan di lingkungan Python 3 untuk bermigrasi ke token ID.

      Meskipun token ID dapat digunakan di runtime Python 2, langkah-langkah di Python 2 bersifat kompleks dan hanya diperlukan sementara hingga Anda mengupdate aplikasi agar berjalan di runtime Python 3.

    2. Setelah aplikasi Anda berjalan di Python 3, update aplikasi untuk meminta token ID dan menambahkan token ke header permintaan.

  2. Di aplikasi yang perlu memverifikasi identitas sebelum memproses permintaan:

    1. Mulailah dengan mengupgrade aplikasi Python 2 Anda untuk mendukung token ID dan identitas App Identity API. Hal ini akan memungkinkan aplikasi Anda memverifikasi dan memproses permintaan dari aplikasi Python 2 yang menggunakan App Identity API atau aplikasi Python 3 yang menggunakan token ID.

    2. Setelah aplikasi Python 2 yang diupgrade stabil, migrasikan ke runtime Python 3. Terus dukung token ID dan identitas App Identity API hingga Anda yakin bahwa aplikasi tidak perlu lagi mendukung permintaan dari aplikasi lama.

    3. Jika Anda tidak perlu lagi memproses permintaan dari aplikasi App Engine lama, hapus kode yang memverifikasi identitas App Identity API.

  3. Setelah menguji aplikasi, deploy aplikasi yang memproses permintaan terlebih dahulu. Kemudian, deploy aplikasi Python 3 terupdate yang menggunakan token ID untuk menyatakan identitas.

Menyatakan identitas

Tunggu hingga aplikasi Anda berjalan di lingkungan Python 3, lalu ikuti langkah-langkah berikut untuk mengupgrade aplikasi guna menyatakan identitas dengan token ID:

  1. Instal google-auth library klien.

  2. Tambahkan kode untuk meminta token ID dari OAuth 2.0 API Google dan menambahkan token ke header permintaan sebelum mengirim permintaan.

  3. Uji update Anda.

Menginstal library klien google-auth untuk aplikasi Python 3

Agar library klien google-auth tersedia untuk aplikasi Python3 Anda, buat file requirements.txt di folder yang sama dengan file app.yaml Anda dan tambahkan baris berikut:

     google-auth

Saat Anda men-deploy aplikasi, App Engine akan mendownload semua dependensi yang ditentukan dalam file requirements.txt.

Untuk pengembangan lokal, sebaiknya instal dependensi di lingkungan virtual seperti venv.

Menambahkan kode untuk menyatakan identitas

Telusuri kode Anda dan temukan semua instance pengiriman permintaan ke aplikasi App Engine lainnya. Perbarui instance tersebut untuk melakukan hal berikut sebelum mengirim permintaan:

  1. Tambahkan impor berikut:

    from google.auth.transport import requests as reqs
    from google.oauth2 import id_token
    
  2. Gunakan google.oauth2.id_token.fetch_id_token(request, audience) untuk mengambil token ID. Sertakan parameter berikut dalam panggilan metode:

    • request: Meneruskan objek permintaan yang akan dikirim.
    • audience: Meneruskan URL aplikasi yang Anda kirimi permintaan. Tindakan ini akan mengikat token ke permintaan dan mencegah token digunakan aplikasi lain.

      Agar jelas dan spesifik, sebaiknya teruskan URL appspot.com yang dibuat App Engine untuk layanan tertentu yang menerima permintaan, meskipun Anda menggunakan domain kustom untuk aplikasi tersebut.

  3. Dalam objek permintaan Anda, tetapkan header berikut:

    'Authorization': 'ID {}'.format(token)
    

Contoh:

# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from flask import Flask, render_template, request
from google.auth.transport import requests as reqs
from google.oauth2 import id_token
import requests

app = Flask(__name__)

@app.route("/", methods=["GET"])
def index():
    return render_template("index.html")

@app.route("/", methods=["POST"])
def make_request():
    url = request.form["url"]
    token = id_token.fetch_id_token(reqs.Request(), url)

    resp = requests.get(url, headers={"Authorization": f"Bearer {token}"})

    message = f"Response when calling {url}:\n\n"
    message += resp.text

    return message, 200, {"Content-type": "text/plain"}

Menguji update untuk menyatakan identitas

Untuk menjalankan aplikasi Anda secara lokal dan menguji apakah aplikasi berhasil mengirim token ID:

  1. Ikuti langkah-langkah berikut agar kredensial akun layanan App Engine default tersedia di lingkungan lokal Anda (Google OAuth API mengharuskan kredensial ini untuk membuat token ID):

    1. Masukkan perintah gcloud berikut untuk mengambil kunci akun layanan untuk akun App Engine default project Anda:

      gcloud iam service-accounts keys create ~/key.json --iam-account project-ID@appspot.gserviceaccount.com

      Ganti project-ID dengan ID project Google Cloud Anda.

      File kunci akun layanan sekarang sudah didownload ke komputer Anda. Anda dapat memindahkan dan mengganti nama file ini sesuai keinginan Anda. Pastikan Anda menyimpan file ini dengan aman, karena file ini dapat digunakan untuk melakukan autentikasi sebagai akun layanan Anda. Jika file hilang, atau jika file diekspos kepada pengguna yang tidak sah, hapus kunci akun layanan dan buat yang baru.

    2. Masukkan perintah berikut:

      <code>export GOOGLE_APPLICATION_CREDENTIALS=<var>service-account-key</var></code>
      

    Ganti service-account-key dengan nama jalur absolut file yang berisi kunci akun layanan yang Anda download.

  2. Di shell yang sama tempat Anda mengekspor variabel lingkungan GOOGLE_APPLICATION_CREDENTIALS, mulai aplikasi Python Anda.

  3. Kirim permintaan dari aplikasi dan pastikan apakah permintaan berhasil. Jika Anda belum memiliki aplikasi yang dapat menerima permintaan dan menggunakan token ID untuk memverifikasi identitas:

    1. Download contoh aplikasi "masuk".
    2. Pada file main.py contoh, tambahkan ID project Google Cloud Anda ke allowed_app_ids. Contoh:

       allowed_app_ids = [
          '<APP_ID_1>',
          '<APP_ID_2>',
          'my-project-id'
        ]
      
    3. Jalankan contoh yang telah diupdate di server pengembangan lokal Python 2.

Memverifikasi dan memproses permintaan

Untuk mengupgrade aplikasi Python 2 Anda agar menggunakan token ID atau identitas App Identity API sebelum memproses permintaan:

  1. Instal library klien autentikasi google.

  2. Update kode Anda untuk melakukan hal berikut:

    1. Jika permintaan berisi header X-Appengine-Inbound-Appid, gunakan header tersebut untuk memverifikasi identitas. Aplikasi yang berjalan di runtime lama seperti Python 2 akan berisi header ini.

    2. Jika permintaan tersebut tidak berisi header X-Appengine-Inbound-Appid, periksa token ID OIDC. Jika token ada, verifikasi payload token dan periksa identitas pengirim.

  3. Uji update Anda.

Menginstal library klien google-auth untuk aplikasi Python 2

Agar library klien google-auth tersedia untuk aplikasi Python 2 Anda:

  1. Buat file requirements.txt di folder yang sama dengan file app.yaml Anda dan tambahkan baris berikut:

     google-auth==1.19.2
    

    Sebaiknya gunakan library klien Cloud Logging versi 1.19.2 karena mendukung aplikasi Python 2.7

  2. Di file app.yaml aplikasi, tentukan library SSL di bagian libraries jika belum ditentukan:

    libraries:
    - name: ssl
      version: latest
    
  3. Buat direktori untuk menyimpan library pihak ketiga, seperti lib/. Lalu, gunakan pip install untuk menginstal library ke dalam direktori. Contoh:

    pip install -t lib -r requirements.txt
    
  4. Buat file appengine_config.py di folder yang sama dengan file app.yaml Anda. Tambahkan kode berikut ke file appengine_config.py Anda:

    # appengine_config.py
    import pkg_resources
    from google.appengine.ext import vendor
    
    # Set path to your libraries folder.
    path = 'lib'
    # Add libraries installed in the path folder.
    vendor.add(path)
    # Add libraries to pkg_resources working set to find the distribution.
    pkg_resources.working_set.add_entry(path)
    

    File appengine_config.py dalam contoh sebelumnya mengasumsikan bahwa folder lib berada di direktori kerja saat ini. Jika Anda tidak dapat menjamin bahwa lib akan selalu berada di direktori kerja saat ini, tentukan jalur lengkap ke folder lib. Contoh:

    import os
    path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lib')
    

Untuk pengembangan lokal, sebaiknya instal dependensi di lingkungan virtual seperti virtualenv untuk Python 2.

Memperbarui kode untuk memverifikasi permintaan

Telusuri kode Anda dan temukan semua instance untuk mendapatkan nilai header X-Appengine-Inbound-Appid. Perbarui instance tersebut untuk melakukan hal berikut:

  1. Tambahkan impor berikut:

    from google.auth.transport import requests as reqs
    from google.oauth2 import id_token
    
  2. Jika permintaan masuk tidak berisi header X-Appengine-Inbound-Appid, cari header Authorization, lalu ambil nilainya.

    Nilai header diformat sebagai "ID: token".

  3. Gunakan google.oauth2.id_token.verify_oauth2_token(token, request, audience) untuk memverifikasi dan mengambil payload token yang didekode. Sertakan parameter berikut dalam panggilan metode:

    • token: Meneruskan token yang diekstrak dari permintaan masuk.
    • request: Meneruskan objek google.auth.transport.Request baru.

    • audience: Meneruskan URL aplikasi saat ini (aplikasi yang mengirimkan permintaan verifikasi). Server otorisasi Google akan membandingkan URL ini dengan URL yang diberikan saat token pertama kali dibuat. Jika URL tidak cocok, token tidak akan diverifikasi dan server otorisasi akan menampilkan error.

  4. Metode verify_oauth2_token menampilkan payload token yang didekode, yang berisi beberapa pasangan nama/nilai, termasuk alamat email akun layanan default untuk aplikasi yang menghasilkan token.

  5. Ekstrak nama pengguna dari alamat email di payload token.

    Nama pengguna sama dengan project ID aplikasi yang mengirim permintaan. Nilai ini sama dengan yang sebelumnya ditampilkan di header X-Appengine-Inbound-Appid.

  6. Jika nama pengguna/project ID ada dalam daftar project ID yang diizinkan, proses permintaan tersebut.

Contoh:

# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Authenticate requests coming from other App Engine instances.
"""

from google.oauth2 import id_token
from google.auth.transport import requests

import logging
import webapp2

def get_app_id(request):
    # Requests from App Engine Standard for Python 2.7 will include a
    # trustworthy X-Appengine-Inbound-Appid. Other requests won't have
    # that header, as the App Engine runtime will strip it out
    incoming_app_id = request.headers.get("X-Appengine-Inbound-Appid", None)
    if incoming_app_id is not None:
        return incoming_app_id

    # Other App Engine apps can get an ID token for the App Engine default
    # service account, which will identify the application ID. They will
    # have to include at token in an Authorization header to be recognized
    # by this method.
    auth_header = request.headers.get("Authorization", None)
    if auth_header is None:
        return None

    # The auth_header must be in the form Authorization: Bearer token.
    bearer, token = auth_header.split()
    if bearer.lower() != "bearer":
        return None

    try:
        info = id_token.verify_oauth2_token(token, requests.Request())
        service_account_email = info["email"]
        incoming_app_id, domain = service_account_email.split("@")
        if domain != "appspot.gserviceaccount.com":  # Not App Engine svc acct
            return None
        else:
            return incoming_app_id
    except Exception as e:
        # report or log if desired, as here:
        logging.warning("Request has bad OAuth2 id token: {}".format(e))
        return None

class MainPage(webapp2.RequestHandler):
    allowed_app_ids = ["other-app-id", "other-app-id-2"]

    def get(self):
        incoming_app_id = get_app_id(self.request)

        if incoming_app_id is None:
            self.abort(403)

        if incoming_app_id not in self.allowed_app_ids:
            self.abort(403)

        self.response.write("This is a protected page.")

app = webapp2.WSGIApplication([("/", MainPage)], debug=True)

Menguji update untuk memverifikasi identitas

Untuk menguji apakah aplikasi Anda dapat menggunakan token ID atau X-Appengine-Inbound-Appid untuk memverifikasi permintaan, jalankan aplikasi di Python 2 server pengembangan lokal dan kirim permintaan dari aplikasi Python 2 (yang akan menggunakan App Identity API) dan dari aplikasi Python 3 yang mengirim token ID.

Jika Anda belum mengupdate aplikasi untuk mengirim token ID:

  1. Download contoh aplikasi "meminta".

  2. Tambahkan kredensial akun layanan ke lingkungan lokal Anda seperti yang dijelaskan dalam Menguji update untuk menyatakan aplikasi.

  3. Gunakan perintah Python 3 standar untuk memulai aplikasi contoh Python 3.

  4. Kirim permintaan dari aplikasi contoh dan konfirmasi apakah permintaan tersebut berhasil.

Men-deploy aplikasi Anda

Setelah siap men-deploy aplikasi, Anda harus:

  1. Menguji aplikasi di App Engine.

  2. Jika aplikasi berjalan tanpa error, gunakan pemisahan traffic untuk meningkatkan traffic aplikasi yang diupdate secara perlahan. Pantau aplikasi dengan cermat untuk menemukan masalah sebelum mengarahkan lebih banyak traffic ke aplikasi yang telah diupdate.

Menggunakan akun layanan yang berbeda untuk menyatakan identitas

Saat Anda meminta token ID, permintaan tersebut secara default akan menggunakan identitas akun layanan default App Engine. Saat Anda memverifikasi token, payload token berisi alamat email akun layanan default, yang dipetakan ke project ID aplikasi Anda.

Akun layanan default App Engine memiliki tingkat izin yang sangat tinggi secara default. Layanan ini dapat melihat dan mengedit seluruh project Google Cloud Anda, sehingga akun ini biasanya tidak sesuai untuk digunakan saat aplikasi Anda perlu melakukan autentikasi dengan layanan Cloud.

Namun, akun layanan default aman digunakan saat menyatakan identitas aplikasi karena Anda hanya menggunakan token ID untuk memverifikasi identitas aplikasi yang mengirim permintaan. Izin sebenarnya yang telah diberikan ke akun layanan tidak dipertimbangkan atau diperlukan selama proses ini.

Jika Anda masih ingin menggunakan akun layanan lain untuk permintaan token ID, lakukan langkah berikut:

  1. Tetapkan variabel lingkungan bernama GOOGLE_APPLICATION_CREDENTIALS ke jalur file JSON yang berisi kredensial akun layanan. Lihat rekomendasi kami untuk menyimpan kredensial ini dengan aman.

  2. Gunakan google.oauth2.id_token.fetch_id_token(request, audience) untuk mengambil token ID.

  3. Saat Anda memverifikasi token ini, payload token akan berisi alamat email akun layanan yang baru.