אימות עם Firebase באמצעות חשבונות מבוססי-סיסמה עם C++

אתם יכולים להשתמש ב-Firebase Authentication כדי לאפשר למשתמשים שלכם לאמת את הזהות שלהם ב-Firebase באמצעות כתובות האימייל והסיסמאות שלהם, וכדי לנהל את החשבונות של האפליקציה שלכם שמבוססים על סיסמאות.

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

  1. איך מוסיפים את Firebase לפרויקט C++‎
  2. אם עדיין לא קישרתם את האפליקציה לפרויקט Firebase, אתם צריכים לעשות זאת דרך מסוף Firebase.
  3. מפעילים את האפשרות 'כתובת אימייל/סיסמה' כספק כניסה לפרויקט Firebase:
    1. במסוף Firebase, עוברים אל Security (אבטחה) > Authentication (אימות).
    2. בכרטיסייה שיטת התחברות, מפעילים את ספק ההזדהות Email/password.
    3. לוחצים על שמירה.

גישה לכיתה firebase::auth::Auth

המחלקות Auth הן שער הכניסה לכל הקריאות ל-API.
  1. מוסיפים את קובצי הכותרת של Auth ו-App:
    #include <cassert>
    #include <cstdint>
    
    #include "firebase/app.h"
    #include "firebase/auth.h"
  2. יוצרים מחלקה firebase::App בקוד האתחול.
    #if defined(__ANDROID__)
      firebase::App* app =
          firebase::App::Create(firebase::AppOptions(), my_jni_env, my_activity);
    #else
      firebase::App* app = firebase::App::Create(firebase::AppOptions());
    #endif  // defined(__ANDROID__)
  3. רוכשים את הכיתה firebase::auth::Auth עבור firebase::App. יש מיפוי של אחד לאחד בין App לבין Auth.
    firebase::auth::Auth* auth = firebase::auth::Auth::GetAuth(app);

יצירת חשבון שמבוסס על סיסמה

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

  1. כשמשתמש חדש נרשם באמצעות טופס ההרשמה של האפליקציה, צריך להשלים את כל שלבי האימות של החשבון החדש שהאפליקציה דורשת, כמו אימות הסיסמה של החשבון החדש כדי לוודא שהיא הוזנה בצורה נכונה ועומדת בדרישות המורכבות שלכם.
  2. יוצרים חשבון חדש על ידי העברת כתובת האימייל והסיסמה של המשתמש החדש אל Auth::CreateUserWithEmailAndPassword:
    firebase::Future<firebase::auth::AuthResult> result =
        auth->CreateUserWithEmailAndPassword(email, password);
  3. אם התוכנית שלכם כוללת לולאת עדכון שפועלת באופן קבוע (למשל 30 או 60 פעמים בשנייה), אתם יכולים לבדוק את התוצאות פעם אחת בכל עדכון באמצעות Auth::CreateUserWithEmailAndPasswordLastResult:
    firebase::Future<firebase::auth::AuthResult> result =
        auth->CreateUserWithEmailAndPasswordLastResult();
    if (result.status() == firebase::kFutureStatusComplete) {
      if (result.error() == firebase::auth::kAuthErrorNone) {
        const firebase::auth::AuthResult auth_result = *result.result();
        printf("Create user succeeded for email %s\n",
               auth_result.user.email().c_str());
      } else {
        printf("Created user failed with error '%s'\n", result.error_message());
      }
    }
    לחלופין, אם התוכנית מבוססת על אירועים, אפשר לרשום קריאה חוזרת ב-Future.

כניסה של משתמש באמצעות כתובת אימייל וסיסמה

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

  1. כשמשתמש נכנס לאפליקציה שלכם, מעבירים את כתובת האימייל והסיסמה של המשתמש אל firebase::auth::Auth::SignInWithEmailAndPassword:
    firebase::Future<firebase::auth::AuthResult> result =
        auth->SignInWithEmailAndPassword(email, password);
  2. אם התוכנית שלכם כוללת לולאת עדכון שפועלת באופן קבוע (למשל 30 או 60 פעמים בשנייה), אתם יכולים לבדוק את התוצאות פעם אחת בכל עדכון באמצעות Auth::SignInWithEmailAndPasswordLastResult:
    firebase::Future<firebase::auth::AuthResult> result =
        auth->SignInWithEmailAndPasswordLastResult();
    if (result.status() == firebase::kFutureStatusComplete) {
      if (result.error() == firebase::auth::kAuthErrorNone) {
        const firebase::auth::AuthResult auth_result = *result.result();
        printf("Sign in succeeded for email %s\n",
               auth_result.user.email().c_str());
      } else {
        printf("Sign in failed with error '%s'\n", result.error_message());
      }
    }
    לחלופין, אם התוכנית מבוססת על אירועים, אפשר לרשום קריאה חוזרת ב-Future.

רישום של התקשרות חזרה ב-Future

יש תוכניות עם פונקציות Update שמופעלות 30 או 60 פעמים בשנייה. לדוגמה, משחקים רבים פועלים לפי המודל הזה. התוכניות האלה יכולות להפעיל את הפונקציות LastResult כדי לבצע סקר של שיחות אסינכרוניות. עם זאת, אם התוכנית שלכם מבוססת על אירועים, יכול להיות שתעדיפו לרשום פונקציות של קריאה חוזרת. פונקציית קריאה חוזרת מופעלת עם השלמת ה-Future.
void OnCreateCallback(const firebase::Future<firebase::auth::User*>& result,
                      void* user_data) {
  // The callback is called when the Future enters the `complete` state.
  assert(result.status() == firebase::kFutureStatusComplete);

  // Use `user_data` to pass-in program context, if you like.
  MyProgramContext* program_context = static_cast<MyProgramContext*>(user_data);

  // Important to handle both success and failure situations.
  if (result.error() == firebase::auth::kAuthErrorNone) {
    firebase::auth::User* user = *result.result();
    printf("Create user succeeded for email %s\n", user->email().c_str());

    // Perform other actions on User, if you like.
    firebase::auth::User::UserProfile profile;
    profile.display_name = program_context->display_name;
    user->UpdateUserProfile(profile);

  } else {
    printf("Created user failed with error '%s'\n", result.error_message());
  }
}

void CreateUser(firebase::auth::Auth* auth) {
  // Callbacks work the same for any firebase::Future.
  firebase::Future<firebase::auth::AuthResult> result =
      auth->CreateUserWithEmailAndPasswordLastResult();

  // `&my_program_context` is passed verbatim to OnCreateCallback().
  result.OnCompletion(OnCreateCallback, &my_program_context);
}
אם רוצים, פונקציית הקריאה החוזרת יכולה להיות גם למדה.
void CreateUserUsingLambda(firebase::auth::Auth* auth) {
  // Callbacks work the same for any firebase::Future.
  firebase::Future<firebase::auth::AuthResult> result =
      auth->CreateUserWithEmailAndPasswordLastResult();

  // The lambda has the same signature as the callback function.
  result.OnCompletion(
      [](const firebase::Future<firebase::auth::User*>& result,
         void* user_data) {
        // `user_data` is the same as &my_program_context, below.
        // Note that we can't capture this value in the [] because std::function
        // is not supported by our minimum compiler spec (which is pre C++11).
        MyProgramContext* program_context =
            static_cast<MyProgramContext*>(user_data);

        // Process create user result...
        (void)program_context;
      },
      &my_program_context);
}

מומלץ: הגדרת מדיניות לסיסמאות

כדי לשפר את אבטחת החשבון, אפשר לאכוף דרישות למורכבות הסיסמה.

כדי להגדיר מדיניות סיסמאות לפרויקט, פותחים את הקטע מדיניות סיסמאות בכרטיסייה הגדרות > אימות > אבטחה במסוף Firebase:

הגדרות אימות

מדיניות הסיסמאות של Firebase Authentication תומכת בדרישות הסיסמה הבאות:

  • נדרשת אות קטנה

  • חובה להשתמש באותיות רישיות

  • נדרש תו מספרי

  • נדרש תו לא אלפאנומרי

    התווים הבאים עומדים בדרישה לתו שאינו אלפאנומרי: ^ $ * . [ ] { } ( ) ? " ! @ # % & / \ , > < ' : ; | _ ~

  • אורך סיסמה מינימלי (הטווח הוא 6 עד 30 תווים, ברירת המחדל היא 6)

  • אורך הסיסמה המקסימלי (אורך מקסימלי של 4,096 תווים)

אפשר להפעיל את אכיפת מדיניות הסיסמאות בשני מצבים:

  • חובה: ניסיונות ההרשמה ייכשלו עד שהמשתמש יעבור לסיסמה שעומדת בדרישות המדיניות.

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

משתמשים חדשים תמיד נדרשים לבחור סיסמה שעומדת בדרישות המדיניות שלכם.

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

מומלץ: הפעלת הגנה מפני ספירת כתובות אימייל

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

כדי לצמצם את הסיכון הזה, מומלץ לוודא שההגנה מפני ספירת כתובות אימייל מופעלת בפרויקט. כדי לבדוק את ההגדרה הזו במסוף Firebase, עוברים אל אבטחה > אימות > הכרטיסייה הגדרות, ואז לוחצים על פעולות משתמש.

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

השלבים הבאים

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

  • באפליקציות שלכם, תוכלו לקבל את פרטי הפרופיל הבסיסיים של המשתמש מאובייקט firebase::auth::User:

    firebase::auth::User user = auth->current_user();
    if (user.is_valid()) {
      std::string name = user.display_name();
      std::string email = user.email();
      std::string photo_url = user.photo_url();
      // The user's ID, unique to the Firebase project.
      // Do NOT use this value to authenticate with your backend server,
      // if you have one. Use firebase::auth::User::Token() instead.
      std::string uid = user.uid();
    }
  • בFirebase Realtime Database ובCloud Storage כללי האבטחה, אפשר לקבל את מזהה המשתמש הייחודי של המשתמש המחובר מהמשתנה auth, ולהשתמש בו כדי לקבוע לאילו נתונים המשתמש יכול לגשת.

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

כדי להוציא משתמש מהחשבון, מתקשרים אל SignOut():

auth->SignOut();