OpenID Connect

אפשר להשתמש בממשקי ה-API של Google OAuth 2.0 גם לאימות וגם להרשאה. במסמך הזה מתואר יישום OAuth 2.0 שלנו לאימות, שתואם למפרט OpenID Connect ומאושר על ידי OpenID. התיעוד שמופיע במאמר שימוש ב-OAuth 2.0 לגישה ל-Google APIs רלוונטי גם לשירות הזה. אם רוצים לבדוק את הפרוטוקול הזה באופן אינטראקטיבי, מומלץ להשתמש ב-Google OAuth 2.0 Playground. כדי לקבל עזרה ב-Stack Overflow, צריך לתייג את השאלות בתג google-oauth.

הגדרת OAuth 2.0

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

קבלת פרטי כניסה של OAuth 2.0

אתם צריכים פרטי כניסה של OAuth 2.0, כולל מזהה לקוח וסוד לקוח, כדי לאמת משתמשים ולקבל גישה לממשקי ה-API של Google.

כדי לראות את מזהה הלקוח ואת הסוד של הלקוח עבור פרטי כניסה מסוג OAuth 2.0, לוחצים על הטקסט הבא: Select credential. בחלון שנפתח, בוחרים את הפרויקט ואת פרטי הכניסה הרצויים ולוחצים על View (הצגה).

אפשר גם לראות את מזהה הלקוח ואת סוד הלקוח מתוך Clients page ב-Cloud Console:

  1. Go to the Clients page.
  2. לוחצים על שם הלקוח או על סמל העריכה (). מזהה הלקוח והסוד שלו מופיעים בחלק העליון של הדף.

הגדרת URI של הפניה אוטומטית

ה-URI להפניה אוטומטית שאתם מגדירים ב- Cloud Console קובע לאן Google שולחת תשובות לבקשות האימות שלכם.

כדי ליצור, להציג או לערוך את כתובות ה-URI להפניה אוטומטית של פרטי כניסה מסוג OAuth 2.0, צריך לבצע את הפעולות הבאות:

  1. Go to the Clients page.
  2. לוחצים על הלקוח.
  3. צפייה ב-URI של ההפניה או עריכה שלו.

אם אין לקוח שמופיע בדף Clients (לקוחות), סימן שלפרויקט שלכם אין פרטי כניסה מסוג OAuth. כדי ליצור לקוח, לוחצים על יצירת לקוח.

התאמה אישית של מסך בקשת ההסכמה

חוויית האימות של OAuth 2.0 כוללת מסך הסכמה שבו מתואר המידע שהמשתמש משחרר ואת התנאים הרלוונטיים. לדוגמה, כשמשתמש מתחבר, יכול להיות שהוא יתבקש לתת לאפליקציה שלכם גישה לכתובת האימייל ולפרטים הבסיסיים של החשבון שלו. אתם מבקשים גישה למידע הזה באמצעות הפרמטר scope, שהאפליקציה שלכם כוללת בבקשת האימות שלה. אפשר גם להשתמש בהיקפי הרשאות כדי לבקש גישה לממשקי Google API אחרים.

במסך בקשת ההסכמה של המשתמש מוצג גם מידע מיתוגי כמו שם המוצר, הלוגו וכתובת האתר של דף הבית. אתם קובעים את פרטי המיתוג ב- Cloud Console.

כדי להפעיל את מסך ההסכמה של הפרויקט:

  1. פותחים את Branding page ב- Google Cloud Console.
  2. If prompted, select a project, or create a new one.
  3. ממלאים את הטופס ולוחצים על שמירה.

בתיבת הדו-שיח להבעת הסכמה שמוצגת בהמשך אפשר לראות מה משתמש יראה כשבקשה כוללת שילוב של היקפי הרשאות של OAuth 2.0 ושל Google Drive. (תיבת הדו-שיח הגנרית הזו נוצרה באמצעות Google OAuth 2.0 Playground, ולכן היא לא כוללת פרטי מיתוג שיוגדרו ב- Cloud Console).

דוגמה לדף הסכמה
איור 1. צילום מסך של דף בקשת ההסכמה

גישה לשירות

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

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

אימות המשתמש

אימות המשתמש כולל קבלת אסימון מזהה ואימות שלו. אסימונים מזהים הם תכונה סטנדרטית של OpenID Connect שנועדה לשימוש בשיתוף הצהרות זהות באינטרנט.

הגישות הנפוצות ביותר לאימות משתמש ולקבלת אסימון מזהה נקראות זרימת 'השרת' וזרימת 'הענקת גישה משתמעת'. בתהליך בצד השרת, השרת לקצה העורפי של האפליקציה מאמת את הזהות של האדם שמשתמש בדפדפן או במכשיר נייד. הזרימה המרומזת משמשת כשאפליקציה בצד הלקוח (בדרך כלל אפליקציית JavaScript שפועלת בדפדפן) צריכה לגשת ישירות לממשקי API במקום להשתמש בשרת הקצה העורפי שלה.

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

תהליך העבודה של השרת

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

  1. יצירת טוקן למניעת זיוף בקשות
  2. שליחת בקשת אימות ל-Google
  3. אישור של טוקן למניעת זיוף
  4. המרת code לאסימון גישה ולאסימון מזהה
  5. קבלת פרטי משתמש מטוקן המזהה
  6. אימות המשתמש

1. יצירת טוקן מצב למניעת זיוף

עליכם להגן על אבטחת המשתמשים שלכם על ידי מניעת התקפות זיוף בקשות. השלב הראשון הוא ליצור אסימון סשן ייחודי שמכיל את הסטטוס בין האפליקציה לבין הלקוח של המשתמש. בהמשך, אתם משווים את טוקן הסשן הייחודי הזה לתגובת האימות שמוחזרת על ידי שירות הכניסה באמצעות OAuth של Google, כדי לוודא שהמשתמש הוא זה ששולח את הבקשה ולא תוקף זדוני. האסימונים האלה נקראים לעיתים קרובות אסימוני CSRF (cross-site request forgery, זיוף בקשות חוצות אתרים).

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

הקוד הבא מדגים איך ליצור אסימוני סשן ייחודיים.

PHP

כדי להשתמש בדוגמה הזו, צריך להוריד את ספריית הלקוח של Google APIs ל-PHP.

// Create a state token to prevent request forgery.
// Store it in the session for later validation.
$state = bin2hex(random_bytes(128/8));
$app['session']->set('state', $state);
// Set the client ID, token state, and application name in the HTML while
// serving it.
return $app['twig']->render('index.html', array(
    'CLIENT_ID' => CLIENT_ID,
    'STATE' => $state,
    'APPLICATION_NAME' => APPLICATION_NAME
));

Java

כדי להשתמש בדוגמה הזו, צריך להוריד את ספריית הלקוח של Google APIs ל-Java.

// Create a state token to prevent request forgery.
// Store it in the session for later validation.
String state = new BigInteger(130, new SecureRandom()).toString(32);
request.session().attribute("state", state);
// Read index.html into memory, and set the client ID,
// token state, and application name in the HTML before serving it.
return new Scanner(new File("index.html"), "UTF-8")
    .useDelimiter("\\A").next()
    .replaceAll("[{]{2}\\s*CLIENT_ID\\s*[}]{2}", CLIENT_ID)
    .replaceAll("[{]{2}\\s*STATE\\s*[}]{2}", state)
    .replaceAll("[{]{2}\\s*APPLICATION_NAME\\s*[}]{2}",
    APPLICATION_NAME);

Python

כדי להשתמש בדוגמה הזו, צריך להוריד את ספריית הלקוח של Google APIs ל-Python.

# Create a state token to prevent request forgery.
# Store it in the session for later validation.
state = hashlib.sha256(os.urandom(1024)).hexdigest()
session['state'] = state
# Set the client ID, token state, and application name in the HTML while
# serving it.
response = make_response(
    render_template('index.html',
                    CLIENT_ID=CLIENT_ID,
                    STATE=state,
                    APPLICATION_NAME=APPLICATION_NAME))

2. שליחת בקשת אימות ל-Google

השלב הבא הוא יצירת בקשת HTTPS GET עם פרמטרים מתאימים של URI. שימו לב שבכל השלבים בתהליך הזה נעשה שימוש ב-HTTPS ולא ב-HTTP. חיבורי HTTP נדחים. צריך לאחזר את כתובת ה-URI הבסיסית ממסמך הגילוי באמצעות ערך המטא-נתונים authorization_endpoint. בדיון הבא נניח שכתובת ה-URI הבסיסית היא https://accounts.google.com/o/oauth2/v2/auth.

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

  • client_id, שאפשר לקבל מ- Cloud Console Clients page .
  • response_type, שבבקשה בסיסית להרשאה באמצעות קוד צריך להיות code. (מידע נוסף זמין בכתובת response_type).
  • scope, שבבקשה בסיסית צריך להיות openid email. (מידע נוסף זמין בכתובת scope).
  • redirect_uri צריכה להיות נקודת הקצה של HTTP בשרת שלכם שתקבל את התגובה מ-Google. הערך חייב להיות זהה לאחת מכתובות ה-URI המורשות להפניה אוטומטית של לקוח OAuth 2.0, שהגדרתם ב Cloud Console Credentials page. אם הערך הזה לא תואם ל-URI מורשה, הבקשה תיכשל ותוצג השגיאה redirect_uri_mismatch.
  • state צריך לכלול את הערך של טוקן הסשן הייחודי למניעת זיוף, וגם כל מידע אחר שנדרש לשחזור ההקשר כשהמשתמש חוזר לאפליקציה, למשל כתובת ה-URL של הדף הראשון. (מידע נוסף זמין בכתובת state).
  • nonce הוא ערך אקראי שנוצר על ידי האפליקציה ומאפשר הגנה מפני שידור חוזר, אם הוא קיים.
  • login_hint יכול להיות כתובת האימייל של המשתמש או המחרוזת sub, ששווה למזהה Google של המשתמש. אם לא מספקים login_hint והמשתמש מחובר לחשבון, מסך ההסכמה כולל בקשה לאישור לשחרור כתובת האימייל של המשתמש לאפליקציה. (מידע נוסף זמין במאמר login_hint).
  • משתמשים בפרמטר hd כדי לבצע אופטימיזציה של תהליך OpenID Connect למשתמשים בדומיין מסוים שמשויך לארגון Google Workspace או Cloud (מידע נוסף זמין בכתובת hd).

דוגמה ל-URI מלא של אימות OpenID Connect, עם מעברי שורה ורווחים כדי שיהיה קל לקרוא אותו:

https://accounts.google.com/o/oauth2/v2/auth?
 response_type=code&
 client_id=424911365001.apps.googleusercontent.com&
 scope=openid%20email&
 redirect_uri=https%3A//oauth2.example.com/code&
 state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foauth2-login-demo.example.com%2FmyHome&
 login_hint=jsmith@example.com&
 nonce=0394852-3190485-2490358&
 hd=example.com

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

3. אישור של טוקן למניעת זיוף

התגובה נשלחת לכתובת redirect_uri שציינתם בבקשה. כל התשובות מוחזרות במחרוזת השאילתה:

https://oauth2.example.com/code?state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foa2cb.example.com%2FmyHome&code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&scope=openid%20email%20https://www.googleapis.com/auth/userinfo.email

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

הקוד הבא מדגים איך מאשרים את אסימוני הסשן שיצרתם בשלב 1:

PHP

כדי להשתמש בדוגמה הזו, צריך להוריד את ספריית הלקוח של Google APIs ל-PHP.

// Ensure that there is no request forgery going on, and that the user
// sending us this connect request is the user that was supposed to.
if ($request->get('state') != ($app['session']->get('state'))) {
  return new Response('Invalid state parameter', 401);
}

Java

כדי להשתמש בדוגמה הזו, צריך להוריד את ספריית הלקוח של Google APIs ל-Java.

// Ensure that there is no request forgery going on, and that the user
// sending us this connect request is the user that was supposed to.
if (!request.queryParams("state").equals(
    request.session().attribute("state"))) {
  response.status(401);
  return GSON.toJson("Invalid state parameter.");
}

Python

כדי להשתמש בדוגמה הזו, צריך להוריד את ספריית הלקוח של Google APIs ל-Python.

# Ensure that the request is not a forgery and that the user sending
# this connect request is the expected user.
if request.args.get('state', '') != session['state']:
  response = make_response(json.dumps('Invalid state parameter.'), 401)
  response.headers['Content-Type'] = 'application/json'
  return response

4. החלפת code באסימון גישה ובאסימון מזהה

התגובה כוללת פרמטר code, קוד הרשאה חד-פעמי שהשרת יכול להמיר לאסימון גישה ולאסימון מזהה. השרת מבצע את ההחלפה הזו על ידי שליחת בקשת HTTPS‏ POST. בקשת POST נשלחת לנקודת הקצה של האסימון, שאותה צריך לאחזר ממסמך הגילוי באמצעות ערך המטא-נתונים token_endpoint. בדיון הבא אנחנו מניחים שנקודת הקצה היא https://oauth2.googleapis.com/token. הבקשה חייבת לכלול את הפרמטרים הבאים בגוף POST:

שדות
code קוד ההרשאה שמוחזר מהבקשה הראשונית.
client_id מזהה הלקוח שמתקבל מ- Cloud Console Clients page, כמו שמתואר במאמר בנושא קבלת פרטי כניסה ל-OAuth 2.0.
client_secret הסוד של הלקוח שמתקבל מ- Cloud Console Clients page, כפי שמתואר במאמר בנושא קבלת פרטי כניסה ל-OAuth 2.0.
redirect_uri כתובת URI מורשית להפניה אוטומטית עבור client_id שצוין ב- Cloud Console Clients page, כמו שמתואר במאמר הגדרת כתובת URI להפניה אוטומטית.
grant_type השדה הזה חייב להכיל את הערך authorization_code,‏ כפי שמוגדר במפרט OAuth 2.0.

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

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your-client-id&
client_secret=your-client-secret&
redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code

תגובה מוצלחת לבקשה הזו מכילה את השדות הבאים במערך JSON:

שדות
access_token אסימון שאפשר לשלוח ל-Google API.
expires_in משך החיים שנותר של אסימון הגישה בשניות.
id_token JWT שמכיל פרטי זהות של המשתמש, שעליו חתמה Google באופן דיגיטלי.
scope היקפי הגישה שניתנו על ידי access_token מוצגים כרשימה של מחרוזות שמופרדות ברווחים, והן תלויות רישיות.
token_type מזהה את סוג האסימון שמוחזר. בשלב הזה, השדה הזה תמיד מכיל את הערך Bearer.
refresh_token (אופציונלי)

השדה הזה מופיע רק אם הפרמטר access_type הוגדר לערך offline בבקשת האימות. פרטים נוספים זמינים במאמר בנושא אסימוני רענון.

5. קבלת מידע על המשתמש מטוקן המזהה

אסימון מזהה הוא JWT (‏JSON Web Token), כלומר אובייקט JSON בקידוד Base64 שחתום באופן קריפטוגרפי. בדרך כלל, חשוב מאוד לאמת אסימון מזהה לפני שמשתמשים בו, אבל מכיוון שאתם מתקשרים ישירות עם Google דרך ערוץ HTTPS ללא מתווך ומשתמשים בסוד הלקוח כדי לאמת את עצמכם ב-Google, אתם יכולים להיות בטוחים שהאסימון שאתם מקבלים באמת מגיע מ-Google ותקף. אם השרת מעביר את אסימון ה-ID לרכיבים אחרים באפליקציה, חשוב מאוד שהרכיבים האחרים יבדקו את תקינות האסימון לפני שישתמשו בו.

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

המטען הייעודי (payload) של אסימון מזהה

טוקן ID הוא אובייקט JSON שמכיל קבוצה של צמדי שם/ערך. דוגמה לפורמט קריא:

{
  "iss": "https://accounts.google.com",
  "azp": "1234987819200.apps.googleusercontent.com",
  "aud": "1234987819200.apps.googleusercontent.com",
  "sub": "10769150350006150715113082367",
  "at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q",
  "hd": "example.com",
  "email": "jsmith@example.com",
  "email_verified": "true",
  "iat": 1353601026,
  "exp": 1353604926,
  "nonce": "0394852-3190485-2490358"
}

אסימונים מזהים של Google עשויים להכיל את השדות הבאים (שנקראים הצהרות):

מימוש הוגדרו תיאור
aud תמיד הקהל שאליו מיועד אסימון הזהות הזה. המזהה צריך להיות אחד ממזהי הלקוח ב-OAuth 2.0 של האפליקציה.
exp תמיד תאריך התפוגה שאחריו אסור לקבל את טוקן המזהה. מיוצג בזמן ראשית יוניקס (מספר שלם של שניות).
iat תמיד השעה שבה הונפק אסימון הזהות. מוצג כזמן יוניקס (מספר של שניות).
iss תמיד מזהה המנפיק של המנפיק של התשובה. תמיד צריך להשתמש ב-https://accounts.google.com או ב-accounts.google.com לאסימונים מזהים של Google.
sub תמיד מזהה של המשתמש, ייחודי בין כל חשבונות Google ולא נעשה בו שימוש חוזר. לחשבון Google יכולות להיות כמה כתובות אימייל בנקודות זמן שונות, אבל הערך של sub אף פעם לא משתנה. משתמשים ב-sub באפליקציה בתור מפתח המזהה הייחודי של המשתמש. אורך מקסימלי של 255 תווים ב-ASCII, תלוי אותיות רישיות.
auth_time הזמן שבו התבצע אימות המשתמש, מספר בפורמט JSON שמייצג את מספר השניות שחלפו מאז ראשית זמן יוניקס (1 בינואר 1970, 00:00:00 UTC). הערך הזה מסופק אם התביעה auth_time כלולה בבקשת האימות ומופעלת בהגדרות.
at_hash גיבוב (hash) של טוקן הגישה. מספק אימות שטוקן הגישה קשור לטוקן הזהות. אם אסימון המזהה מונפק עם ערך access_token בתהליך בצד השרת, ההצהרה הזו תמיד נכללת. אפשר להשתמש בטענה הזו כמנגנון חלופי להגנה מפני מתקפות של זיוף בקשות חוצות אתרים, אבל אם פועלים לפי שלב 1 ושלב 3, אין צורך לאמת את אסימון הגישה.
azp client_id של המציג המורשה. הטענה הזו נדרשת רק אם הצד שמבקש את טוקן ה-ID הוא לא אותו צד שמוגדר כקהל של טוקן ה-ID. יכול להיות שזה המצב ב-Google באפליקציות היברידיות שבהן לאפליקציית אינטרנט ולאפליקציית Android יש client_id שונה של OAuth 2.0, אבל הן חולקות את אותו פרויקט Google APIs.
email כתובת האימייל של המשתמש. הערך הזה מסופק רק אם כללתם את היקף ההרשאות email בבקשה. הערך של הטענה הזו לא בהכרח ייחודי לחשבון הזה, והוא עשוי להשתנות עם הזמן. לכן, לא מומלץ להשתמש בערך הזה כמזהה הראשי לקישור לרשומת המשתמש. בנוסף, אי אפשר להסתמך על הדומיין של טענת email כדי לזהות משתמשים בארגונים ב-Google Workspace או ב-Cloud. במקום זאת, צריך להשתמש בטענת hd.
email_verified הערך יהיה True אם כתובת האימייל של המשתמש אומתה, ו-False אם לא.
family_name שם המשפחה של המשתמש. אפשר לציין זאת כשיש תביעה מסוג name.
given_name השם הפרטי של המשתמש. אפשר לציין זאת כשיש תביעה מסוג name.
hd הדומיין שמשויך לארגון Google Workspace או Cloud של המשתמש. הערך הזה מסופק רק אם המשתמש שייך לארגון ב-Google Cloud. צריך לסמן את הטענה הזו כשמגבילים את הגישה למשאב רק לחברים בדומיינים מסוימים. היעדר הטענה הזו מצביע על כך שהחשבון לא שייך לדומיין שמארח Google.
locale הלוקאל של המשתמש, שמיוצג על ידי תג שפה מסוג BCP 47. אפשר לציין זאת כשיש תביעה מסוג name.
name השם המלא של המשתמש, בפורמט שניתן להצגה. אפשר לציין זאת כשהפעולה היא:
  • היקף הבקשה כלל את המחרוזת 'פרופיל'
  • אסימון המזהה מוחזר מרענון של אסימון

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

nonce הערך של nonce שסופק על ידי האפליקציה בבקשת האימות. כדי להגן מפני התקפות שליחה מחדש, צריך להציג את הערך הזה רק פעם אחת.
picture כתובת ה-URL של תמונת הפרופיל של המשתמש. אפשר לציין זאת כשהפעולה היא:
  • היקף הבקשה כלל את המחרוזת 'פרופיל'
  • אסימון המזהה מוחזר מרענון של אסימון

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

profile כתובת ה-URL של דף הפרופיל של המשתמש. אפשר לציין זאת כשהפעולה היא:
  • היקף הבקשה כלל את המחרוזת 'פרופיל'
  • אסימון המזהה מוחזר מרענון של אסימון

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

6. אימות המשתמש

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

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

נושאים מתקדמים

בקטעים הבאים מוסבר על Google OAuth 2.0 API בפירוט רב יותר. המידע הזה מיועד למפתחים עם דרישות מתקדמות בנוגע לאימות ולהרשאה.

גישה לממשקי API אחרים של Google

אחד היתרונות של שימוש ב-OAuth 2.0 לאימות הוא שהאפליקציה יכולה לקבל הרשאה להשתמש בממשקי API אחרים של Google בשם המשתמש (כמו YouTube,‏ Google Drive,‏ Calendar או אנשי קשר) בו-זמנית עם אימות המשתמש. כדי לעשות את זה, צריך לכלול את ההיקפים האחרים שנדרשים בבקשת האימות ששולחים ל-Google. לדוגמה, כדי להוסיף את קבוצת הגיל של המשתמש לבקשת האימות, צריך להעביר פרמטר scope של openid email https://www.googleapis.com/auth/profile.agerange.read. המשתמש מקבל הנחיה מתאימה במסך ההסכמה. אסימון הגישה שתקבלו מ-Google יאפשר לאפליקציה שלכם לגשת לכל ממשקי ה-API שקשורים להיקפי הגישה שביקשתם וקיבלתם.

אסימוני רענון

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

שיקולים:

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

מידע נוסף זמין במאמר בנושא רענון של אסימון גישה (גישה במצב אופליין).

כדי לבקש מהמשתמש לאשר מחדש את הגישה לאפליקציה, צריך להגדיר את הפרמטר prompt לערך consent בבקשת האימות. אם prompt=consent כלול, מסך ההסכמה מוצג בכל פעם שהאפליקציה מבקשת הרשאה להיקפי גישה, גם אם כל ההיקפים כבר הוענקו לפרויקט Google APIs. לכן, כדאי להשתמש ב-prompt=consent רק כשצריך.

מידע נוסף על הפרמטר prompt מופיע בטבלה פרמטרים של URI לאימות, בשורה prompt.

פרמטרים של URI לאימות

בטבלה הבאה מפורטים תיאורים מלאים יותר של הפרמטרים שמתקבלים על ידי Google's OAuth 2.0 authentication API.

פרמטר חובה תיאור
client_id (נדרש) מחרוזת מזהה הלקוח שמתקבלת מ- Cloud Console Clients page, כמו שמתואר במאמר בנושא קבלת פרטי כניסה של OAuth 2.0.
nonce (נדרש) ערך אקראי שנוצר על ידי האפליקציה ומאפשר הגנה מפני הפעלה חוזרת.
response_type (נדרש) אם הערך הוא code, מתחיל תהליך בסיסי של קוד הרשאה, שבו נדרש POST לנקודת הקצה של האסימון כדי לקבל את האסימונים. אם הערך הוא token id_token או id_token token, מופעל תהליך משתמע שדורש שימוש ב-JavaScript בכתובת ה-URI להפניה אוטומטית כדי לאחזר אסימונים ממזהה URI #fragment.
redirect_uri (נדרש) קובע לאן התשובה תישלח. הערך של הפרמטר הזה צריך להיות זהה לאחד מערכי ההפניה האוטומטית המורשים שהגדרתם ב- Cloud Console(כולל סכימת ה-HTTP או ה-HTTPS, האותיות הרישיות והקטנות והסלאש בסוף, אם יש). Clients page
scope (נדרש)

פרמטר ההיקף חייב להתחיל בערך openid ואז לכלול את הערך profile, את הערך email או את שניהם.

אם קיים ערך ההיקף profile, יכול להיות שטוקן ה-ID יכלול את התביעות של profile שמוגדרות כברירת מחדל עבור המשתמש (אבל אין בכך ערובה).

אם הערך של ההיקף email קיים, אסימון ה-ID כולל את התביעות email ו-email_verified.

בנוסף להיקפי ההרשאות הספציפיים ל-OpenID, ארגומנט היקף ההרשאות יכול לכלול גם ערכים אחרים של היקפי הרשאות. צריך להפריד בין כל הערכים של היקף ההרשאה באמצעות רווח. לדוגמה, אם רוצים גישה לכל קובץ ב-Google Drive של משתמש, פרמטר ההיקף יכול להיות openid profile email https://www.googleapis.com/auth/drive.file.

למידע על היקפי ההרשאות הזמינים, אפשר לעיין במאמר היקפי ההרשאות של OAuth 2.0 ל-Google APIs או במסמכי התיעוד של Google API שבו רוצים להשתמש.

state (אופציונלי, אבל מומלץ מאוד)

מחרוזת אטומה שמועברת הלוך ושוב בפרוטוקול. כלומר, היא מוחזרת כפרמטר של URI בתהליך הבסיסי, וכמזהה URI #fragment בתהליך המרומז.

state יכול להיות שימושי כדי לקשר בין בקשות ותשובות. מכיוון שאפשר לנחש את הערך של redirect_uri, שימוש בערך state יכול להגדיל את הסיכוי שחיבור נכנס הוא תוצאה של בקשת אימות שיזמה האפליקציה שלכם. אם תיצרו מחרוזת אקראית או תקודדו את הגיבוב של מצב לקוח מסוים (למשל, קובץ Cookie) במשתנה state הזה, תוכלו לאמת את התשובה כדי לוודא שהבקשה והתשובה הגיעו מאותו דפדפן. ההגדרה הזו מספקת הגנה מפני התקפות כמו זיוף בקשות בין אתרים.

access_type (אופציונלי) הערכים המותרים הם offline ו-online. ההשפעה מתועדת במאמר גישה אופליין. אם מתבצעת בקשה לטוקן גישה, הלקוח לא מקבל טוקן רענון אלא אם מצוין הערך offline.
claims (אופציונלי) הפרמטר claims משמש לציון שדה אופציונלי אחד או יותר שייכללו בנקודת הקצה של userinfo או בסוגי התגובות של אסימון המזהה של בקשת האימות. הערך הוא אובייקט JSON שמכיל את סוג התגובה ואת הטענות המבוקשות. הבקשות הבאות לבעלות על ערוץ מתקבלות בשרתים של Google:
בקשות לתלונה
auth_time שליחת בקשה לקבלת השעה שבה המשתמש אומת לאחרונה. כדי להחזיר את הערך auth_time כשדה בתגובה של אסימון המזהה, צריך לכלול את פרמטר הבקשה claims: claims={"id_token":{"auth_time":{"essential":true}}} צריך להפעיל את האפשרות הזו בהגדרות.
display (אופציונלי) ערך מחרוזת ASCII שמציין איך שרת ההרשאות מציג את דפי ממשק המשתמש של האימות וההסכמה. הערכים הבאים מצוינים ומתקבלים על ידי שרתי Google, אבל אין להם השפעה על התנהגות זרימת הפרוטוקול: page,‏ popup,‏ touch ו-wap.
hd (אופציונלי)

לייעל את תהליך הכניסה לחשבונות שנמצאים בבעלות של ארגון ב-Google Cloud. אם כוללים את הדומיין של הארגון ב-Google Cloud (לדוגמה, mycollege.edu), אפשר לציין שממשק המשתמש לבחירת חשבון צריך להיות מותאם לחשבונות בדומיין הזה. כדי לבצע אופטימיזציה לחשבונות ארגוניים ב-Google Cloud באופן כללי ולא רק לדומיין ארגוני אחד ב-Google Cloud, צריך להגדיר ערך של כוכבית (*): hd=*.

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

include_granted_scopes (אופציונלי) אם הפרמטר הזה מסופק עם הערך true, ובקשת ההרשאה מאושרת, ההרשאה תכלול את כל ההרשאות הקודמות שניתנו לשילוב הזה של משתמש/אפליקציה עבור היקפי הרשאות אחרים. מידע נוסף זמין במאמר בנושא הרשאות מצטברות.

שימו לב: אי אפשר להשתמש בהרשאה מצטברת בתהליך של אפליקציה מותקנת.

login_hint (אופציונלי) כשהאפליקציה יודעת איזה משתמש מנסה לבצע אימות, היא יכולה לספק את הפרמטר הזה כרמז לשרת האימות. העברת הרמז הזה משביתה את כלי בחירת החשבון וממלאת מראש את תיבת האימייל בטופס הכניסה או בוחרת את הסשן המתאים (אם המשתמש משתמש בכניסה מרובה). כך אפשר להימנע מבעיות שמתרחשות אם האפליקציה מתחברת לחשבון משתמש שגוי. הערך יכול להיות כתובת אימייל או המחרוזת sub, שהיא שוות ערך למזהה Google של המשתמש.
prompt (אופציונלי) רשימה מופרדת בפסיקים של ערכי מחרוזת שמציינת אם שרת ההרשאות מציג למשתמש בקשה לאימות מחדש ולהסכמה. הערכים האפשריים הם:
  • none

    שרת ההרשאות לא מציג מסכי אימות או מסכי הסכמה של המשתמשים. אם המשתמש לא מאומת או שלא הגדיר מראש הסכמה להיקפי ההרשאות המבוקשים, השרת יחזיר שגיאה. אפשר להשתמש ב-none כדי לבדוק אם יש אימות או הסכמה קיימים.

  • consent

    שרת ההרשאות מבקש מהמשתמש הסכמה לפני שהוא מחזיר מידע ללקוח.

  • select_account

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

אם לא מציינים ערך והמשתמש לא אישר בעבר גישה, מוצג למשתמש מסך בקשת הסכמה.

hl (אופציונלי) תג שפה BCP 47 שמשמש לציון שפת התצוגה במסכי הכניסה, בחירת החשבון והסכמה. אם לא מספקים את הפרמטר הזה, שפת ברירת המחדל היא השפה שמוגדרת בחשבון Google של המשתמש או בהגדרות הדפדפן שלו. לדוגמה, כדי לבקש את ממשק המשתמש באנגלית בריטית, צריך להגדיר את הפרמטר לערך en-GB.

אימות של אסימון מזהה

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

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

  • שליחת אסימונים מזהים עם בקשות שצריך לאמת. אסימוני המזהה מציינים את המשתמש הספציפי ששולח את הבקשה ואת הלקוח שעבורו הוענק אסימון המזהה.

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

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

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

  1. מוודאים שאסימון ה-ID חתום בצורה תקינה על ידי המנפיק. אסימונים שהונפקו על ידי Google חתומים באמצעות אחד מהאישורים שנמצאים בכתובת ה-URI שצוינה בערך המטא-נתונים jwks_uri של מסמך הגילוי.
  2. מוודאים שהערך של הטענה iss בטוקן המזהה שווה ל-https://accounts.google.com או ל-accounts.google.com.
  3. מוודאים שהערך של התביעה aud בטוקן המזהה שווה למזהה הלקוח של האפליקציה.
  4. מוודאים שתוקף האסימון (claim exp) של מזהה האסימון לא פג.
  5. אם ציינתם ערך של פרמטר hd בבקשה, צריך לוודא שלטוקן ה-ID יש טענה hd שתואמת לדומיין מקובל שמשויך לארגון ב-Google Cloud.

בשלבים 2 עד 5 יש רק השוואות של מחרוזות ותאריכים, שהן די פשוטות, ולכן לא נפרט אותן כאן.

השלב הראשון מורכב יותר וכולל בדיקה של חתימה קריפטוגרפית. למטרות ניפוי באגים, אפשר להשתמש בנקודת הקצה tokeninfo של Google כדי להשוות את התוצאות לעיבוד מקומי שהוטמע בשרת או במכשיר. נניח שהערך של אסימון הזהות הוא XYZ123. אחר כך מבטלים את ההפניה של ה-URI https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123. אם החתימה של הטוקן תקינה, התגובה תהיה המטען הייעודי (payload) של ה-JWT בפורמט של אובייקט JSON מפוענח.

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

מכיוון ש-Google משנה את המפתחות הציבוריים שלה רק לעיתים רחוקות, אפשר לשמור אותם במטמון באמצעות הנחיות המטמון של תגובת ה-HTTP. ברוב המקרים, אפשר לבצע אימות מקומי בצורה יעילה יותר מאשר באמצעות נקודת הקצה tokeninfo. כדי לבצע את האימות הזה, צריך לאחזר ולנתח אישורים, ולבצע את הקריאות הקריפטוגרפיות המתאימות כדי לבדוק את החתימה. למזלנו, יש ספריות שעברו ניפוי באגים וזמינות במגוון רחב של שפות כדי לבצע את הפעולה הזו (ראו jwt.io).

קבלת פרטים מפרופיל המשתמש

כדי לקבל פרטי פרופיל נוספים על המשתמש, אפשר להשתמש באסימון הגישה (שהאפליקציה מקבלת במהלך תהליך האימות) ובסטנדרט OpenID Connect:

  1. כדי לעמוד בדרישות של OpenID, צריך לכלול את ערכי ההיקף openid profile בבקשת האימות.

    אם רוצים לכלול את כתובת האימייל של המשתמש, אפשר לציין ערך נוסף של היקף ההרשאות email. כדי לציין גם את profile וגם את email, אפשר לכלול את הפרמטר הבא ב-URI של בקשת האימות:

    scope=openid%20profile%20email
  2. מוסיפים את אסימון הגישה לכותרת ההרשאה ושולחים בקשת HTTPS‏ GET לנקודת הקצה userinfo. כדי לאחזר את נקודת הקצה הזו, צריך להשתמש בערך המטא-נתונים userinfo_endpoint ממסמך הגילוי. התגובה userinfo כוללת מידע על המשתמש, כפי שמתואר בOpenID Connect Standard Claims ובערך המטא-נתונים claims_supported של מסמך הגילוי. משתמשים או הארגונים שלהם יכולים לבחור אם לספק מידע בשדות מסוימים או לא, ולכן יכול להיות שלא תקבלו מידע בכל שדה בהיקפי הגישה המורשים שלכם.

מסמך Discovery

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

כדי לפשט את ההטמעות ולהגדיל את הגמישות, OpenID Connect מאפשר שימוש ב'מסמך איתור', מסמך JSON שנמצא במיקום ידוע ומכיל צמדי מפתח/ערך שמספקים פרטים על ההגדרה של ספק OpenID Connect, כולל כתובות ה-URI של נקודות הקצה של ההרשאה, האסימון, הביטול, פרטי המשתמש והמפתחות הציבוריים. אפשר לאחזר את מסמך הגילוי של שירות OpenID Connect של Google מהכתובת:

https://accounts.google.com/.well-known/openid-configuration

כדי להשתמש בשירותי OpenID Connect של Google, צריך להטמיע את ה-URI של מסמך הגילוי (https://accounts.google.com/.well-known/openid-configuration) באפליקציה. האפליקציה מאחזרת את המסמך, מחילה כללי שמירה במטמון בתגובה, ואז מאחזרת ממנו מזהי URI של נקודות קצה לפי הצורך. לדוגמה, כדי לאמת משתמש, הקוד יאחזר את ערך המטא-נתונים authorization_endpoint (https://accounts.google.com/o/oauth2/v2/auth בדוגמה שלמטה) כ-URI הבסיסי לבקשות אימות שנשלחות אל Google.

זו דוגמה למסמך כזה. שמות השדות הם אלה שצוינו בOpenID Connect Discovery 1.0 (המשמעויות שלהם מפורטות במסמך הזה). הערכים הם להמחשה בלבד ועשויים להשתנות, למרות שהם מועתקים מגרסה עדכנית של מסמך הגילוי בפועל של Google:

{
  "issuer": "https://accounts.google.com",
  "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
  "device_authorization_endpoint": "https://oauth2.googleapis.com/device/code",
  "token_endpoint": "https://oauth2.googleapis.com/token",
  "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
  "revocation_endpoint": "https://oauth2.googleapis.com/revoke",
  "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
  "response_types_supported": [
    "code",
    "token",
    "id_token",
    "code token",
    "code id_token",
    "token id_token",
    "code token id_token",
    "none"
  ],
  "subject_types_supported": [
    "public"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "scopes_supported": [
    "openid",
    "email",
    "profile"
  ],
  "token_endpoint_auth_methods_supported": [
    "client_secret_post",
    "client_secret_basic"
  ],
  "claims_supported": [
    "aud",
    "email",
    "email_verified",
    "exp",
    "family_name",
    "given_name",
    "iat",
    "iss",
    "locale",
    "name",
    "picture",
    "sub"
  ],
  "code_challenge_methods_supported": [
    "plain",
    "S256"
  ]
}

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

ספריות לקוח

ספריות הלקוח הבאות משולבות עם מסגרות פופולריות, ולכן הן מפשטות את ההטמעה של OAuth 2.0:

תאימות ל-OpenID Connect

מערכת האימות של Google OAuth 2.0 תומכת בתכונות הנדרשות של מפרט OpenID Connect Core. כל לקוח שמיועד לעבוד עם OpenID Connect צריך להיות מסוגל לפעול עם השירות הזה (למעט אובייקט בקשת OpenID).