כניסה של משתמשים ב-App Engine

במדריך הזה נסביר איך לאחזר, לאמת ולאחסן פרטי כניסה של צד שלישי באמצעות Identity Platform, הסביבה הרגילה של App Engine ו-Datastore.

במאמר הזה נסביר איך ליצור אפליקציה פשוטה לרישום הערות בשם Firenotes, שמאחסנת את ההערות של המשתמשים בפנקסים אישיים משלהם. מחברות מאוחסנות לכל משתמש, ומזוהות לפי מזהה ייחודי של Identity Platform של כל משתמש. האפליקציה כוללת את הרכיבים הבאים:

  • חלק הקצה הקדמי מגדיר את ממשק המשתמש לכניסה ומאחזר את מזהה Identity Platform. הוא גם מטפל בשינויים במצב האימות ומאפשר למשתמשים לראות את ההערות שלהם.

  • FirebaseUI הוא פתרון קוד פתוח שניתן להטמעה בקלות, ומפשט את משימות האימות וממשק המשתמש. ה-SDK מטפל בכניסת משתמשים, בקישור של כמה ספקים לחשבון אחד, בשחזור סיסמאות ועוד. הוא מטמיע שיטות מומלצות לאימות, כדי לספק חוויית כניסה חלקה ומאובטחת.

  • הקצה העורפי מאמת את מצב האימות של המשתמש ומחזיר את פרטי פרופיל המשתמש ואת ההערות של המשתמש.

האפליקציה מאחסנת את פרטי הכניסה של המשתמש ב-Datastore באמצעות ספריית הלקוח NDB, אבל אפשר לאחסן את פרטי הכניסה במסד נתונים שתבחרו.

‫Firenotes מבוסס על מסגרת אפליקציית האינטרנט Flask. האפליקציה לדוגמה משתמשת ב-Flask בגלל הפשטות וקלות השימוש שלה, אבל המושגים והטכנולוגיות שנבדקים רלוונטיים לכל framework שבה משתמשים.

מטרות

במדריך הזה תלמדו:

  • הגדרת ממשק המשתמש באמצעות FirebaseUI ל-Identity Platform.
  • מקבלים אסימון מזהה מ-Identity Platform ומאמתים אותו באמצעות אימות בצד השרת.
  • אחסון של פרטי הכניסה של המשתמשים והנתונים המשויכים ב-Datastore.
  • שליחת שאילתה למסד נתונים באמצעות ספריית הלקוח NDB.
  • פריסת אפליקציה ב-App Engine.

עלויות

במדריך הזה נעשה שימוש ברכיבים של Google Cloudשחלים עליהם חיובים, כולל:

  • Datastore
  • Identity Platform

אפשר להשתמש במחשבון עלויות כדי ליצור הערכת עלויות בהתאם לשימוש החזוי.

משתמשים חדשים ב- Google Cloud ? יכול להיות שאתם זכאים לתקופת ניסיון בחינם.

לפני שמתחילים

  1. מתקינים את Git,‏ Python 2.7 ו- virtualenv. מידע נוסף על הגדרת סביבת הפיתוח בשפת Python, כמו התקנת הגרסה העדכנית של Python, מופיע במאמר הגדרת סביבת פיתוח בשפת Python ל- Google Cloud.
  2. נכנסים לחשבון Google Cloud . אם אתם משתמשים חדשים ב- Google Cloud, צרו חשבון כדי שתוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
  3. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  4. התקינו את ה-CLI של Google Cloud.

  5. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

  6. כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:

    gcloud init
  7. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  8. התקינו את ה-CLI של Google Cloud.

  9. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

  10. כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:

    gcloud init

אם כבר התקנתם את ה-SDK והפעלתם אותו בפרויקט אחר, צריך להגדיר את gcloud project למזהה פרויקט App Engine שבו אתם משתמשים ב-Firenotes. במאמר ניהול ההגדרות של Google Cloud SDK מפורטות פקודות ספציפיות לעדכון פרויקט באמצעות הכלי gcloud.

שיבוט של האפליקציה לדוגמה

כדי להוריד את הדוגמה למחשב המקומי:

  1. משכפלים את מאגר האפליקציה לדוגמה ומעבירים אותו למכונה המקומית:

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    אפשרות נוספת היא להוריד את קובץ הדוגמה כקובץ ZIP ולחלץ אותו.

  2. עוברים לספרייה שמכילה את הקוד לדוגמה:

    cd python-docs-samples/appengine/standard/firebase/firenotes
    

הוספת ממשק המשתמש

כדי להגדיר את FirebaseUI ל-Identity Platform ולהפעיל ספקי זהויות:

  1. כדי להוסיף את Identity Platform לאפליקציה:

    1. עוברים אל Google Cloud המסוף.
      כניסה למסוף Google Cloud
    2. בוחרים את הפרויקט Google Cloud שרוצים להשתמש בו:
      • אם יש לכם פרויקט קיים, בוחרים אותו מהרשימה הנפתחת Select organization בחלק העליון של הדף.
      • אם אין לכם פרויקט קיים ב- Google Cloud , אתם יכולים ליצור פרויקט חדש במסוףGoogle Cloud .
    3. נכנסים לדף Identity Platform Marketplace במסוף Google Cloud .
      כניסה לדף Identity Platform Marketplace
    4. בדף Identity Platform Marketplace, לוחצים על Enable Customer Identity (הפעלת זהות לקוחות).
    5. עוברים לדף Users של Customer Identity במסוף Google Cloud .
      כניסה לדף Users
    6. בפינה השמאלית העליונה, לוחצים על סמל האפליקציה פרטי ההגדרה.
    7. מעתיקים את פרטי ההגדרה של האפליקציה לאפליקציית האינטרנט.

      // This code is for illustration purposes only.
      
      // Obtain the following from the "Add Firebase to your web app" dialogue
      // Initialize Firebase
      var config = {
        apiKey: "<API_KEY>",
        authDomain: "<PROJECT_ID>.firebaseapp.com",
        databaseURL: "https://<DATABASE_NAME>.firebaseio.com",
        projectId: "<PROJECT_ID>",
        storageBucket: "<BUCKET>.appspot.com",
        messagingSenderId: "<MESSAGING_SENDER_ID>"
      };
  2. עורכים את הקובץ backend/app.yaml כדי להוסיף את GOOGLE_CLOUD_PROJECT : 'PROJECT_ID' בקטע env_variables:

    # Copyright 2021 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.
    
    # This code is designed for Python 2.7 and
    # the App Engine first-generation Runtime which has reached End of Support.
    
    runtime: python27
    api_version: 1
    threadsafe: true
    service: backend
    
    handlers:
    - url: /.*
      script: main.app
    
    env_variables:
      GAE_USE_SOCKETS_HTTPLIB : 'true'
    
  3. בקובץ frontend/main.js, מגדירים את ווידג'ט הכניסה של FirebaseUI על ידי בחירת הספקים שרוצים להציע למשתמשים.

    // This code is for illustration purposes only.
    
    // Firebase log-in widget
    function configureFirebaseLoginWidget() {
      var uiConfig = {
        'signInSuccessUrl': '/',
        'signInOptions': [
          // Leave the lines as is for the providers you want to offer your users.
          firebase.auth.GoogleAuthProvider.PROVIDER_ID,
          firebase.auth.FacebookAuthProvider.PROVIDER_ID,
          firebase.auth.TwitterAuthProvider.PROVIDER_ID,
          firebase.auth.GithubAuthProvider.PROVIDER_ID,
          firebase.auth.EmailAuthProvider.PROVIDER_ID
        ],
        // Terms of service url
        'tosUrl': '<your-tos-url>',
      };
    
      var ui = new firebaseui.auth.AuthUI(firebase.auth());
      ui.start('#firebaseui-auth-container', uiConfig);
    }
  4. במסוף Google Cloud , מפעילים את הספקים שבחרתם לשמור:

    1. נכנסים לדף Providers של Customer Identity במסוף Google Cloud .
      כניסה לדף Providers
    2. לוחצים על הוספת ספק.
    3. ברשימה הנפתחת בחירת ספק, בוחרים את הספקים שרוצים להשתמש בהם.
    4. לצד מופעל, לוחצים על הלחצן כדי להפעיל את הספק.
      • במקרה של ספקי זהויות של צד שלישי, מזינים את מזהה הספק ואת הסוד מאתר המפתחים של הספק. במסמכי Firebase יש הוראות ספציפיות בקטעים 'לפני שמתחילים' במדריכים בנושא Facebook,‏ Twitter ו-GitHub.
      • באינטגרציות של SAML ו-OIDC, צריך לעיין בהגדרות ב-IdP.
  5. מוסיפים את הדומיין לרשימת הדומיינים המורשים ב-Identity Platform:

    1. עוברים לדף Settings (הגדרות) של Customer Identity (זהות לקוחות) במסוף Google Cloud .
      מעבר לדף ההגדרות
    2. בקטע דומיינים מורשים, לוחצים על הוספת דומיין.
    3. מזינים את הדומיין של האפליקציה בפורמט הבא:

      [PROJECT_ID].appspot.com
      

      לא כוללים את http:// לפני שם הדומיין.

יחסי תלות בהתקנות

  1. מנווטים לספרייה backend ומשלימים את הגדרת האפליקציה:

    cd backend/
    
  2. מתקינים את יחסי התלות בספרייה lib בפרויקט:

    pip install -t lib -r requirements.txt
    
  3. ב-appengine_config.py, השיטה vendor.add() רושמת את הספריות בספרייה lib.

הרצת האפליקציה באופן מקומי

כדי להריץ את האפליקציה באופן מקומי, משתמשים בשרת הפיתוח המקומי של App Engine:

  1. מוסיפים את כתובת ה-URL הבאה כbackendHostURL ב-main.js:

    http://localhost:8081

  2. עוברים לספריית הבסיס של האפליקציה. לאחר מכן, מפעילים את שרת הפיתוח:

    dev_appserver.py frontend/app.yaml backend/app.yaml
    
  3. עוברים אל http://localhost:8080/ בדפדפן אינטרנט.

אימות משתמשים בשרת

אחרי שמגדירים פרויקט ומפעילים אפליקציה לפיתוח, אפשר לעיין בקוד כדי להבין איך לאחזר ולאמת אסימוני מזהה של Identity Platform בשרת.

קבלת אסימון מזהה מ-Identity Platform

השלב הראשון באימות בצד השרת הוא אחזור של טוקן גישה לאימות. בקשות אימות מטופלות באמצעות onAuthStateChanged() listener מ-Identity Platform:

firebase.auth().onAuthStateChanged(function (user) {
  if (user) {
    $('#logged-out').hide();
    var name = user.displayName;

    /* If the provider gives a display name, use the name for the
    personal welcome message. Otherwise, use the user's email. */
    var welcomeName = name ? name : user.email;

    user.getIdToken().then(function (idToken) {
      userIdToken = idToken;

      /* Now that the user is authenicated, fetch the notes. */
      fetchNotes();

      $('#user').text(welcomeName);
      $('#logged-in').show();

    });

  } else {
    $('#logged-in').hide();
    $('#logged-out').show();

  }
});

כשמשתמש מחובר לחשבון, ה-method של Identity Platform‏ getToken() ב-callback מחזיר טוקן מזהה של Identity Platform בפורמט של JSON Web Token‏ (JWT).

אימות טוקנים בשרת

אחרי שהמשתמש מתחבר, שירות הקצה הקדמי מאחזר את כל ההערות הקיימות בפנקס הרשימות של המשתמש באמצעות בקשת AJAX‏ GET. כדי לגשת לנתוני המשתמש נדרשת הרשאה, ולכן ה-JWT נשלח בכותרת Authorization של הבקשה באמצעות סכימת Bearer:

// Fetch notes from the backend.
function fetchNotes() {
  $.ajax(backendHostUrl + '/notes', {
    /* Set header for the XMLHttpRequest to get data from the web server
    associated with userIdToken */
    headers: {
      'Authorization': 'Bearer ' + userIdToken
    }
  })

לפני שהלקוח יכול לגשת לנתוני השרת, השרת צריך לוודא שהטוקן חתום על ידי Identity Platform. אפשר לאמת את האסימון הזה באמצעות ספריית האימות של Google ל-Python. משתמשים בפונקציה verify_firebase_token של ספריית האימות כדי לאמת את אסימון ה-Bearer ולחלץ את הטענות:

# This code is for illustration purposes only.

id_token = request.headers["Authorization"].split(" ").pop()
claims = google.oauth2.id_token.verify_firebase_token(
    id_token, HTTP_REQUEST, audience=os.environ.get("GOOGLE_CLOUD_PROJECT")
)
if not claims:
    return "Unauthorized", 401

כל ספק זהויות שולח קבוצה שונה של הצהרות, אבל לכל אחד מהם יש לפחות הצהרה עם מזהה משתמש ייחודי והצהרה שמספקת מידע מסוים על הפרופיל, כמו name או email, שבהם אפשר להשתמש כדי להתאים אישית את חוויית המשתמש באפליקציה.sub

ניהול נתוני משתמשים ב-Datastore

אחרי אימות המשתמש, צריך לשמור את הנתונים שלו כדי שהם יישארו זמינים גם אחרי שסשן הכניסה מסתיים. בקטעים הבאים מוסבר איך לאחסן הערה כישות ב-Datastore, ולהפריד בין ישויות לפי מזהה משתמש.

יצירת ישויות לאחסון נתוני משתמשים

אפשר ליצור ישות ב-Datastore על ידי הצהרה על מחלקת מודל NDB עם מאפיינים מסוימים כמו מספרים שלמים או מחרוזות. ‫Datastore מבצע אינדוקס של ישויות לפי סוג. במקרה של Firenotes, הסוג של כל ישות הוא Note. לצורך שאילתות, כל Note נשמר עם שם מפתח, שהוא ה-User ID שהתקבל מהטענה sub בקטע הקודם.

בדוגמת הקוד הבאה אפשר לראות איך מגדירים מאפיינים של ישות, גם באמצעות שיטת הבנאי של מחלקת המודל כשיוצרים את הישות, וגם באמצעות הקצאה של מאפיינים נפרדים אחרי היצירה:

# This code is for illustration purposes only.

data = request.get_json()

# Populates note properties according to the model,
# with the user ID as the key name.
note = Note(parent=ndb.Key(Note, claims["sub"]), message=data["message"])

# Some providers do not provide one of these so either can be used.
note.friendly_id = claims.get("name", claims.get("email", "Unknown"))

כדי לכתוב את Note שנוצר לאחרונה ב-Datastore, צריך לבצע קריאה ל-method‏ put() באובייקט note.

אחזור נתוני משתמשים

כדי לאחזר נתוני משתמש שמשויכים למזהה משתמש מסוים, משתמשים בשיטה NDB query() כדי לחפש במסד הנתונים הערות באותה קבוצת ישויות. ישויות באותה קבוצה, או נתיב אב, חולקות שם מפתח משותף, שבמקרה הזה הוא מזהה המשתמש.

# This code is for illustration purposes only.

def query_database(user_id):
    """Fetches all notes associated with user_id.

    Notes are ordered them by date created, with most recent note added
    first.
    """
    ancestor_key = ndb.Key(Note, user_id)
    query = Note.query(ancestor=ancestor_key).order(-Note.created)
    notes = query.fetch()

    note_messages = []

    for note in notes:
        note_messages.append(
            {
                "friendly_id": note.friendly_id,
                "message": note.message,
                "created": note.created,
            }
        )

    return note_messages

לאחר מכן אפשר לאחזר את נתוני השאילתה ולהציג את ההערות בלקוח:

// This code is for illustration purposes only.

// Fetch notes from the backend.
function fetchNotes() {
  $.ajax(backendHostUrl + '/notes', {
    /* Set header for the XMLHttpRequest to get data from the web server
    associated with userIdToken */
    headers: {
      'Authorization': 'Bearer ' + userIdToken
    }
  }).then(function (data) {
    $('#notes-container').empty();
    // Iterate over user data to display user's notes from database.
    data.forEach(function (note) {
      $('#notes-container').append($('<p>').text(note.message));
    });
  });
}

פריסת האפליקציה

הטמעתם בהצלחה את Identity Platform באפליקציית App Engine. כדי לראות את האפליקציה שלכם פועלת בסביבת ייצור פעילה:

  1. משנים את כתובת ה-URL של המארח בקצה העורפי ב-main.js ל-https://backend-dot-[PROJECT_ID].appspot.com. מחליפים את [PROJECT_ID] במזהה הפרויקט.
  2. פורסים את האפליקציה באמצעות ממשק שורת הפקודה של Google Cloud SDK:

    gcloud app deploy backend/index.yaml frontend/app.yaml backend/app.yaml
    
  3. אפשר לצפות בשידור חי של הטקס בכתובת https://[PROJECT_ID].appspot.com.

הסרת המשאבים

כדי להימנע מחיובים בחשבון Google Cloud בגלל השימוש במשאבים שנעשה במסגרת המדריך הזה, אפשר למחוק את פרויקט App Engine:

מחיקת הפרויקט

הדרך הקלה ביותר לבטל את החיוב היא למחוק את הפרויקט שיצרתם בשביל המדריך.

כדי למחוק את הפרויקט:

  1. במסוף Google Cloud , נכנסים לדף Manage resources.

    כניסה לדף Manage resources

  2. ברשימת הפרויקטים, בוחרים את הפרויקט שרוצים למחוק ולוחצים על Delete.
  3. כדי למחוק את הפרויקט, כותבים את מזהה הפרויקט בתיבת הדו-שיח ולוחצים על Shut down.

המאמרים הבאים

  • כדאי להעמיק את הקריאה ולהכיר דוגמאות לארכיטקטורות, תרשימים ושיטות מומלצות בנושאי Google Cloud. כל אלה זמינים במרכז הארכיטקטורה של Cloud.